设计线程安全的类
在设计现车让安全类的过程之中,需要包含以下三步:
- 找出构成对象状态的所有变量
- 找出约束状态变量的不变性条件
- 建立对象状态的并发访问策略
实例封闭
通过封闭机制与合适的加锁策略结合起来,可以确保以线程安全的方式来使用非线程安全的对象。
在Java的类库中,有些类的唯一用途就是讲非线程安全的类转化为线程安全的类。一些基本的容器类并非是线程安全的,例如ArrayList和HashMap,但是可以通过包装器工厂方法,例如Collections.synchronizedList
及其类似方法,使得这些非线程安全的类可以在多线程环境中安全的使用。这些工厂方法通过装饰器模式将容器类封装在一个同步的包装器对象中。
Java监视器模式
遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。示例代码如下所示:
public final class Counter{
private long value=0;
public synchronized long getValue(){
return value;
}
public synchronized long increment(){
if(value==Long.MAX_VALUE)
throw new IllegalStateException("count overflow");
return ++value;
}
}
复制代码
在许多类中都使用了Java监视器模式,例如Vector和Hashtable。
线程安全性的委托
如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。
如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。
委托是实现线程安全的一个最有效策略。委托就是把原来通过使用synchronized和volatile来实现线程安全,委托给Java提供的一些基础构建模块(或者自己写的基础构建模块)区实现,包括:
- 同步容器:比如Vector,但是同步容器的原理大多就是synchronized,所以用的不多
- 并发容器:比如ConcurrentHashMap,CopyonWriteArrayList,LinkedBlockingQueue等。
- 同步公布类:Future,FutureTask还要闭锁(Latches),信号量(Semaphores),栅栏(Barriers)等