竞态条件
第四章 对象的组合
通过各种组合模式让一个类是线程安全的.
设计线程安全的类
步骤:
- 找出构成对象状态的所有变量.
- 找出约束变量状态的"不变性条件".
- 建立对象状态的并发访问管理策略.
原子性和封装性来保证线程安全.
- 如果需要在状态的变换过程中添加条件约束(先检查后执行), 那么就需要利用到原子性和封装性来保证线程安全. 避免检查之后的状态变量被其他线程更改.
- 为了保证有效值, 如更新小汽车的位置x, y时, 更新x,y时必须是原子的, 不然可能会导致出现更新了x之后更新y时, x被其他线程更改了的情况发生.
状态所有权
类对其所封装的状态拥有所有权. 对其发布的状态最多拥有共享控制权, 大多从构造函数和方法中传递进来的对象, 类通常不拥有这些对象.
容器类的 "所有权分离"
容器类拥有其自身状态的控制权, 而客户代码才拥有容器中的各个对象的状态. 如ServletContext
, 其拥有类似的map的上下文容器对象, 但是并不拥有ServletContext
中的包含的对象, 要安全的使用存储在ServletContext
中的对象, 必须保证下面其中之一的条件: 对象是线程安全的, 对象是事实不可变的, 对象是由锁保护的.
通过 实例封闭 的形式保证线程安全.
将状态数据封装在对象内部. 对所有访问该对象的路径都进行线程安全的策略配置. 对象的状态完全由其内置锁来保护.
私有锁
私有的锁对象可以将锁封装起来, 客户代码无法获得到锁.
线程的委托
将对象的线程安全委托给一个或多个线程安全的不可变对象. 若是委托给多个对象, 则要求这多个对象之间是没有关系的, 如果有联系, 则需要锁来同步操作.
在现有的线程安全类中添加功能.
比如添加一个'如果不存在就添加'的功能. 肯定是要先检查, 再执行的, 为了保证线程安全, 这两步必须是原子的. 那么如何实现呢.
- 修改原始类: 基本很难实现, 因为原始类有可能在jar包中或者有其他原因不能更改.
- 扩展原始类: 方式并不是很牢靠, 因为要求原始类的状态对子类是开放的, 日后基类的同不策略发生变化可能会影响到子类的, 类的功能维护在了子类中.
- 客户端加锁: 要使用这个方法对某个X对象加锁, 则必须知道X对象使用了什么锁. 2和3都破坏了封装性
- 组合: 例如对某个List对象X进行添加功能, 则将X赋值给组合类的内部List引用, 组合类采用自身的内置锁增加了额外一层的锁. 这种模式更加健壮.
网友评论