在设计线程安全类的过程中,需要包含以下三个基本要素:
可以将共享资源委托给一个线程安全的类。比如ConcurrentHashMap,copyOnWriteArrayList.
如果一个类时由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层状态变量。
下面是一个监控车辆位置的实例。其中Point是线程安全不可变的类。
/**
* 不可变
*/
@Immutable
class Point{
public final int x,y;
Point(int x,int y ) {
this.x = x;
this.y = y;
}
}
/**
* 委托给线程安全类的车辆追踪器
*/
@ThreadSafe
class DelegatingVehicleTracker{
private final ConcurrentHashMap<String,Point> locations;
private final Map<String,Point> unmodifiableMap;
public DelegatingVehicleTracker(Map<String,Point> points) {
this.locations = new ConcurrentHashMap<>(points);
this.unmodifiableMap = Collections.unmodifiableMap(locations);
}
public Map<String,Point> getLocations(){
return unmodifiableMap;
}
public Point getLocation(String id){
return locations.get(id);
}
public void setLocation(String id,int x,int y){
if(locations.replace(id,new Point(x,y)) == null){
throw new IllegalArgumentException("invalid vehicle name:"+id);
}
}
}
如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。
同样是车辆追踪,我想要获取位置,还可以修改位置,安全性问题可以交给底层SafePoint:
/**
* 线程安全且可变的Point类
*/
@ThreadSafe
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
class PublishingVehicleTracker{
private final Map<String,SafePoint> locations;
private final Map<String,SafePoint> unmodifiableMap;
PublishingVehicleTracker(Map<String, SafePoint> locations, Map<String, SafePoint> unmodifiableMap) {
this.locations = locations;
this.unmodifiableMap = unmodifiableMap;
}
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 vehicle name:"+id);
locations.get(id).set(x,y);
}
}
在文档中说明客户代码需要了解的线程安全性保证,以及代码维护人员需要了解的同步策略。
synchronized,volatile或者任何一个线程安全类都对应于某种同步策略,用于在并发访问时确保数据的完整性。一定要在忘记之前记录下来。
可以使用@GuardedBy("this")或者别的来注释锁。