目录
1.设计线程安全的类
2.实例封闭
3.线程安全性的委托
4.在现有的线程安全类中添加功能
1.设计线程安全的类
通过使用封装技术,可以使得在不对整个程序进行分析的情况下就可以判断一个类是否是线程安全的。
在设计线程安全类的过程中,需要包含以下三个基本要素:
-找出构成对象状态的所有变量。
-找出约束状态变量的不可变性条件。
-建立对象状态的并发访问管理策略。
下面这个类的状态就是 value 这个变量
package com.javaConcurrency;
public class codelist4_1 {
@Threadsafe
public final class Counter{
@GuardedBy("this") private long value = 0;
public synchronized long getValue(){
return value;
}
public synchronized long increment(){
if(value == Long.MAX_VALUE)
throw new IllegalStateException("counter overflow");
return ++value;
}
}
}
2.实例封闭
如果对象不是线程安全的,可以通过多种技术使其在多线程中安全使用,你可以确保该对象只能由单个线程访问(线程封闭),或者通过锁来保护该对象的所有访问。
下面实例说明了如何通过封闭与加锁等机制使一个类成为线程安全的
@Threadsafe
public class PersonSet{
@GuardedBy("this")
private final Set<Person> mySet = new HashSet<Person>();
public sychronized void addPerson(Person p){
mySet.add(p);
}
public Synchronized boolean containsPerson(Person p){
return mySet.contains(p);
}
Java 监视器模式
从现成封闭原则及其逻辑推论可以得出 Java 监视器模式。遵循 Java 监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。
public class PrivateLock{
private final Object myLock = new Object();
@GuardedBy("myLock") Widget widget;
void someMethod(){
synchronized(myLock){
//访问或修改 Widget 的状态
}
}
}
示例:车辆追踪
package com.javaConcurrency;
public class codelist4_4 {
@ThreadSafe
public class MonitorVehicleTracker{
@GuardedBy("this")
private final Map<String,MutablePoint> locations;
public MonitorVehicleTracker(Map<String, MutablePoint> locations){
this.locations = locations;
}
public synchronized Map<String,MutablePoint> getLocations(){
return deepCopy(locations);
}
public synchronized MutablePoint getLocation(String id){
MutablePoint loc = locations.get(id);
return loc == null ? null : new MutablePoint(loc);
}
public synchronized void setLocations(String id, int x,int y){
MutablePoint loc = locations.get(id);
if(loc == null)
throw new IllegalArgumentException("No such Id: " + id);
loc.x = x;
loc.y = y;
}
}
@NotThreadSafe
public class MutablePoint{
public int x,y;
public MutablePoint(){x = 0;y = 0;}
public MutablePoint(MutablePoint p){
this.x = x;
this.y = y;
}
}
}
虽然 MutablePoint 是线程不安全的,但是追踪器是安全的,它所包含的 Map 对象都不曾发布,返回车辆信息的时候,都是 deepCopy 来复制生成新的 Map 对象。
3.线程安全性的委托
构造一个给线程安全类的车辆追踪器,将车辆位置保存在 Map 中 用一个不可变类 Point 类来代替 MutablePoint 保存位置。
@Immutable
public class Piont{
public final int x,y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
@ThreadSafe
public class DelegatingVehicleTracker{
private final ConcurrentHashMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
@ThreadSafe
public class DelegatingVehicleTracker{
private final ConcurrentHashMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
public DelegatingVehicleTracker(Map<String,Point> points){
locations = new ConcurrentHashMap<String, Point>(points);
unmodifiableMap = Collections.unmodifiableMap(locations);
}
public Map<String, Point> getLocations() {
return unmodifiableMap;
}
public Point getLocation(String id){
return locations.get(id);
}
public void setLocations(String id, int x, int y){
if(locations.replace(id, new Point(x,y)) == null)
throw new IllegalArgumentException("invalid vehicle name: " + id);
}
}
将线程安全性委托给多个状态变量
允许客户注册监控鼠标键盘等事件监听器。
public class VisualComponent{
private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<>();
private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<>();
public void addKeyListener(KeyListener listener){
keyListeners.add(listener);
}
public void addMouseListener(MouseListener listener){
mouseListeners.add(listener);
}
public void removeKeyListener(KeyListener listener){
keyListeners.remove(listener);
}
public void removeMouseListener(MouseListener listener){
mouseListeners.remove(listener);
}
}
示例:发布底层可变状态
发布状态的车辆追踪器
// 线程安全且可变的Point类
@ThreadSafe
public class SafePoint{
@GuardedBy("this") private int x,y;
private SafePoint(int[] a){this(a[0].a[1]);}
public SafePoint(SafePoint p){this(p.get());}
public SafePoint(int x, int y){
this.x = x;
this.y = y;
}
public synchronized int[] get(){
return new int[] {x,y};
}
public synchronized void set(int x, int y){
this.x = x;
this.y = y;
}
}
//安全发布底层状态的车辆追踪器
@ThreadSafe
public class PublishingVehicleTracker{
private final Map<String,SafePoint> locations;
private final Map<String,SafePoint> unmodifiableMap;
public PublishingVehicleTracker(Map<String, SafePoint> locations){
this.locations = new ConcurrentHashMap<String, SafePoint>(locations);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}
public Map<String, SafePoint> getLocations(){
return unmodifiableMap;
}
public SafePoint getLocation(String id){
return locations.get(id);
}
public void setLocation(String id, int x, int y){
if(!locations.containsKey(id))
throw new IllegalArgumentException("invalid vechile name: " + id);
locations.get(id).set(x,y);
}
}
4.从现有的线程安全类中添加功能
扩展一个 putIfAbsent 的功能
@ThreadSafe
public class BetterVector<E> extends Vector<E>{
public synchronized boolean putIfAbsent(E x){
boolean absent = !contains(x);
if(absent)
add(x);
return absent;
}
}
客户端加锁机制
错误的实现方式:
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
为啥呢?毕竟,putIfAbsent 已经声明为synchronized类型的变量,对不对?问题在于在错误的锁上进行了同步。无论List使用哪一个锁来保护它的状态,可以确定的是,这个锁并不是 ListHelper 上的锁。ListHelper 只是带来了同步的假象,尽管所有的链表操作都被声明为synchronized,但却使用了不同的锁,这意味着 putIfAbsent 相对于List的其他操作来说并不是原子的,因此就无法确保当 putIfAbsent 执行时另一个线程不会修改链表。
要想使这个方法能正确执行,必须使List在实现客户端加锁或外部加锁时使用同一个锁。客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户端代码。要使用客户端加锁,你必须知道对象X使用的是哪一个锁。
在Vector和同步封装器类的文档中指出,它们通过使用Vector或封装器容器的内置锁来支持客户端加锁。
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
}
网友评论