Java多线程-并发设计模式
为了解决多线程带来的并发问题,业界总结出的一套设计模式,类似缓存更新设计模式、23种设计模式一样
并发编程领域的问题总结为3个:分工、同步和互斥。同步和互斥相关问题更多源自于微观,分工问题则是源自于宏观。我们在解决问题的时候先从宏观设计,而后再进行微观设计。
一、不可变模式 Immutability
见String Integer,自己实现不可变类需要注意的点
- 类定义成final(不允许被继承)
- 所有属性设置成final,只提供get操作
- 如果属性也是对象,则该对象也应该是不可变的
- 构造时“逸出”?在构造器中将this赋值给全局变量,
二、Copy-on-Write模式
改模式也是不可变模式的一种实现手段,比如string中的replace,实际上返回的是一个新的string对象
CopyOnWriteArrayList
,CopyOnWriteArraySet
,写时复制思想就是在写的时候,赋值一份数据,写完后再赋值给原来的引用。这样能做到读时不加锁,但是新数据不会被立即读到。需要业务承受一定的时间的不一致的。
适用的场景:读多写少
三、线程本地存储模式
将共享资源进行线程封闭
-
放到线程方法内部
-
Thread Local
Thread Local 实现原理,每个线程有个map,ThreadLocalMap,key是ThreadLocal实例,value是存放的值,这样就可以实现一个线程可以任意设置多个value
四、GuardedSuspension模式
保护性暂停,等待唤醒机制的规范实现,典型场景:生产者消费者模型。
多线程版本的if
五、Balking模式
Balking模式本质上是一种规范化地解决“多线程版本的if”的方案 ,
实现方案:
- 利用synchronized
- 利用volatile(如果对原子性没有要求)
Balking模式和Guarded Suspension模式从实现上看似乎没有多大的关系,Balking模式只需要用互斥锁就能 解决,而Guarded Suspension模式则要用到管程这种高级的并发原语;但是从应用的角度来看,它们解决 的都是“线程安全的if”语义,不同之处在于,Guarded Suspension模式会等待if条件为真,而Balking模式 不会等待。
Balking模式的典型应用场景:并发初始化资源,只要初始化一次。
六、Thread-Per-Message模式
为每个任务分配一个独立的线程,最经典的应用场景是网络编程里服务端的实现(线程池只是这个模式的实现上的优化)
但是在Java语言中,线程是一个重对象,主要是其创建和销毁过程重(至于为什么重,后续再研究)。所以在实现上有两种优化方案
- 采用线程池,避免线程频繁的创建和销毁
- 采用更轻量级的线程,在其他语言中叫协程,Java中对其支持的方案是Fiber
七、WorkerThread模式
Thread-Per-Message的改进版,线程池
八、两阶段终止模式
如何优雅地停止线程,在Java中,提供了stop()方法,这个方式会直接终止线程,不给任何执行收尾工作的机会,所以逐渐被过时了,为了能够优雅地停止线程(给线程执行收尾工作),采用两阶段终止模式,第一阶段,向目标线程发送终止指令,第二阶段目标线程响应终止指令。Thread#interrupt().
九、生产者消费者模式
核心方案用队列存储任务,生产者和消费者之间不直接强依赖,解耦。
线程池本身就是生产者消费者模式的实现,