设计线程安全类的过程应该包括下面3个基本要素
1,确定对象状态是由哪些变量构成的
2,确定限制状态变量的不变约束
3,制定一个管理并发访问对象状态的策略
将数据封装在对象内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。
public class PersonSer{
private final Set mySer=new HashSet();
public synchronized void addPerson(Person p){
mySet.add(p);
}
public synchronized boolean containsPerson(Person p){
return mySet.contains(p);
}
}
上面的例子是线程安全的,其中未对
Person
的线程安全性做任何假设,如果他是可变的,那么访问从
PersonSet
中获得的
Person
时,还需要额外的同步。为了安全的使用
Person
对象,最可靠的方法是让
Person
自身是线程安全的,对
Person
对象加锁并不十分可靠,因为他还需要所有的用户都遵守协议:访问
Person
前先获得正确的锁。public class MonitorVehicleTracker{
private final Map locations;
public MonitorVehicleTracker(Map locations){
this.locations=deepCopy(locations);
}
public synchronized Map getLocations(){
MutablePoint loc=locations.get(id);
return loc==null?null:new MutablePoint(loc);
}
public synchronized void setLoaction(String id,int x,int y){
MutablePoint loc=location.get(id);
if(loc==null){
throw new IllegalArgumentException(“No such ID:”+id);
}
loc.x=x;
loc.y=y;
}
private static Map deepCopy(Map m){
Map result = new HashMap();
for(String id:m.keySet()){
result.put(id,new MutablePoint(m.get(id)));
}
return Collections.unmodifiableMap(result);
}
}
public class MutablePoint{
public int x,y;
public MutablePoint(){
x=0;
y=0;
}
public MutablePoint(MutablePoint p){
this.x=x;
this.y=y;
}
}
我们可以使用
Point 代替MutablePoint
public class Point{
public final int x,y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
}
由于
Point
类时不可变的,因而是线程安全的,所以我们返回
location
时不必再复制他们。
使用委托将线程安全交给ConcurrentHashMap
public class DelegatingVehicleTracker{
private final ConcurrentMap locations;
private final Map unmodifiableMap;
public DelegatingVehicleTracker(Map points){
locations=new ConcurrentHashMap(points);
unmodifiableMap=Collections.unmodifiableMap(locations);
}
public Map 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);
}
}
public class VisualComponent{
private final List keyListeners=new CopyOnWriteArrayList();
private final List mouseListeners=new CopyOnWriteArrayList();
pulic 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);
}
}
上面的类使用
CopyOnWriteArrayList
存储每个监听器清单。这个线程安全的
List
实现油气适合管理监听器的清单。其中,不但每个
List
是线程安全的,而且不存在哪个不变约束增加一个状态与另一个状态间耦合。但大多数组合对象不像上面的类那样简单,他们的不变约束与组件的状态变量相互联系。public class NumberRange{
private final AtomicInteger lower=new AtomicInteger(0);
private final AtomicInteger upper=new AtomicInteger(0);
public void setLower(int i){
if(i>upper.get()){
throw new IllegalArgumentException();
}
lower.set(i);
}
public void setUpper(int i){
if(ilower.get()&&i<=upper.get());
}
}
如果一个类由多个彼此独立的线程安全的状态变量组成,并且类的操作不包含任何无效状态转换时,可以讲线程安全委托给这些状态变量。
添加一个新的原子操作有两种方式,一是修改原始的类。二是扩展这个类,假设这个类在设计上是可扩展的。但是并非所有的类都给子类暴露了足够多的状态以支持这种方案。因为扩展后,同步策略的实现会被分布到多个独立维护的源代码中,如果底层的类选择了不同的锁来保护她的状态变量,从而会改变他的同步策略,子类就在不知不觉中被破坏,因为他不能再用正确的锁控制对基类状态的并发访问。
public class BetterVector extends Vector{
public synchronized boolean putIfAbsent(E x){
boolean absent=!contains(x);
if(absent){
add(x);
}
return absent;
}
}
上面的例子是安全的,因为Vector的同步策略已由其规约固定住,所以其子类不会有问题。
对于一个由Collections.synchronizedList封装的ArrayList,两种方法都不正确,因为客户端代码甚至不知道同步封装工厂方法返回的List对象的类型。第三个策略是扩展功能,而不是扩展类本身,并将扩展代码置入一个助手类。
public class ListHelper{
public List list=Collections.synchronizedList(new ArrayList());
pubic synchronized boolean putIfAbsent(E x){
boolean absent=!list.contains(x);
if(absent){
list.add(x);
}
return absent;
}
}
上面的例子不是线程安全的。这里的问题在于同步行为发生在错误的锁上。无论
List
使用哪个锁保护她的状态,可以确定这个锁并没用到
ListHelper
上。
为了保证这个方法正确工作,我们必须保证方法使用的锁与List用于客户端加锁与外部加锁时所用的锁时同一个锁。
public class ListHelper{
public List list=Collections.synchronizedList(new ArrayList());
public boolean putIfAbsent(E x){
synchronized(list){
boolean absent=!list.contains(x);
if(absent){
list.add(x);
}
return absent;
}
}
}
上面的类时线程安全的。
向已有的类中添加一个原子操作,还可以使用组合。
public class ImprovedList implements List{
private final List list;
public ImprovedList(List list){
this.list=list;
}
public synchronized boolean putIfAbsent(T x){
boolean contains=list.contains(x);
if(contains){
list.add(x);
}
return !contains;
}
public synchronized void clear(){
list.clear();
}
}
通过内部锁,
ImprovedList
引入了一个新的锁层。他并不关心底层的
List
是否线程安全,即使
List
不是线程安全的,或者会改变
ImprovedList
的锁实现,
ImprovedList
都有自己兼容的锁可以提供线程安全性,虽然会有一部分性能的损失。