设计线程安全的类
在设计线程安全类的过程中,需要包含以下三个基本要素:
- 找出构成对象状态的所有变量
- 找出约束状态变量的不变性条件
- 建立对象状态的并发访问管理策略
同步策略定义了如何在不违背对象不变性条件或后验条件的情况下对其状态的访问操作进行协同。同步策略规定了如何将不可变性、线程封闭与加锁机制等结合起来以维护线程的安全性,并且还规定了哪些变量由哪些锁来保护。
收集同步需求
不变性条件——判断状态是否有效
后验条件——判断状态迁移是否有效
原子操作的目的是为了避免无效状态的出现。
如果在一个不变性条件中包含多个变量,那么在执行任何访问相关变量的操作时,都必须持有保护这些变量的锁。
如果不了解对象的不变性条件与后验条件,那么就不能确保线程安全性。
要满足在状态变量的有效值或状态转换上的各种约束条件,就需要借助于原子性与封装性。
依赖状态的操作
如果在某个操作中包含有基于状态的先验条件,那么这个操作就称为依赖状态的操作。
状态的所有权
许多情况下,所有权与封装性总是相互关联的:对象封装它拥有的状态,反之也成立,即对它封装的状态拥有所有权。状态变量的所有者将决定采用何种加锁协议来维持变量状态的完整性。所有权意味着控制权。
然而,如果发布了某个可变对象的引用,那么就不再拥有独占的控制权,最多是“共享控制权”。
容器类通常表现出一种“所有权分离”的形式。
实例封闭
封闭与加锁
实例封闭:将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。——构建线程安全类的一个最简单方式
- 封闭在类的一个实例中(例如类的私有成员)
- 封闭在某个作用域内(例如局部变量)
- 封闭在线程内
包装器工厂方法
装饰器模式
封闭机制更易于构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性时就无须检查整个程序。
Java监视器模式
Java监视器模式:把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。
使用私有的锁对象而不是对象的内置锁(或任何其他可通过公有方式访问的锁)。
私有的锁对象可以将锁封装起来,使客户代码无法得到锁。 因为如果客户代码错误地获得了一个对象的锁,那么可能会产生活跃性问题。
线程安全性的委托
如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。
在现有的线程安全类中添加功能
通过组合实现: