那些年读过的书《Java并发编程实战》二、如何设计线程安全类
1、设计线程安全类的过程
设计线程安全类的过程就是设计对象状态并发访问下线程间的协同机制(在不破坏对象状态变量的不变性条件的前提下)。
(1)构建线程安全类的三个基本要素:
1)找出构成对象状态的所有变量;(确定状态变量的类型(共享、可变的、不可变的),针对不同类型的状态变量采用不同的并发访问策略)
2)找出约束对象状态变量的不变性条件;(不变性条件本质上就是确定状态变量自身的约束条件和状态变量间的依赖关系)
对象状态变量的不变性条件与后验条件约束了在对象状态上有哪些状态和状态转换是有效的,也就是对象状态变量的值哪些是有效的,哪些状态之间的转换时有效的,而哪些状态变量值和哪些状态间的转换时无效的。
3)建立对象状态的并发访问管理策略
同步策略:定义了如何在不违背不破坏对象不可变性或后验性的情况下对其状态的访问操作进行协同,规定了如何将不可变性、线程封闭、加锁机制等结合起来已维护线程的安全性,并且还规定了哪些变量由哪些锁来保护。
(2)构建线程安全类的过程
1)收集同步需求
确定构成对象状态的所有变量和对象状态变量不可变性条件的过程。
2)依赖状态的操作
在依赖对象状态的操作上(比如先验条件)如何设计,最简单的办法是通过现有库中的类来实现依赖状态的行为。
3)状态的所有权
确定状态变量如何共享和发布
2、构建线程安全类的三种方法:
(1)构建线程安全类的最简单方式:实例封闭
将数据封装在对象的内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。
封闭机制和加锁机制结合起来,就可以确保以线程安全的方式使用非线程安全的对象
Java监视器模式:封装对象的所有可变状态,并有对象自己的内置锁来保护。
(2)组合多个类构建线程安全类(线程安全性的委托)
1)单个状态变量情况下
将单个状态变量委托给线程安全的类(如ConcurrentHashMap等)
2)多个状态变量且没有不可变性的约束下
每个状态变量都交给一个线程安全类维护
3)多个状态变量且存在不可变性的约束
委托和加锁机制联合起来才能实现在构建线程安全类的过程中不破坏多个状态变量不可变性条件。
4)发布底层的状态变量
如果一个类由多个独立且线程安全的状态变量组成,并且在所有的操作中都不含无效的状态转换,那么可以将线程安全性委托给底层的状态变量。
底层状态变量的发布:如果一个状态变量是线程安全的类,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全的发布这个变量。
(3)在现有线程安全类中添加功能以构建新的线程安全类
1)修改现有线程安全类代码,新增功能
2)扩展现有线程安全类
3)使用辅助类扩展类的功能
4)组合
3、线程安全类的维护
将同步策略文档化
4.构建线程安全性委托的基础构建模块
(1)同步容器类
同步容器只有所有对容器状态的访问操作都串行化,才能实现他们的线程安全性。在并发的环境中,访问容器的状态在客户端没有加锁的情况下不是线程安全的。
实现线程安全的方式:封装容器的状态,把所有公有的的方法都进行同步,使得每次只有一个线程可以访问容器的状态。
缺点:严重降低并发性,吞吐量低
(2)并发容器
(1)ConCurrentHashMap
(2)CopyOnWriteArrayList
(3)阻塞队列和生产者-消费者模式
(1)BlockingQueue 阻塞队列:适用于生产者-消费者模式(消费者共享一个工作队列)
(2)BlockingDeque 阻塞双端队列:适用于工作密取模式(每个消费者都有一个自己的双端队列,当自己的双端队列完成后消费者可以从其他消费者的双端队列内获取)
工作密取模式比生产者-消费者模式更具可伸缩性,因为工作密取的每个消费者都有自己的工作队列,就减少了在因共享队列上因获取的数据而发生的竞争。
(4)阻塞方法和中断方法
(5)同步工具类
(1)CountDownLatch
(2)FutureTask
(3)Semaphore
(4)CyclicBarrier和Exchanger