并发设计模式

Immutability模式

不变性(Immutability)模式,即对象一旦被创建之后,状态就不再发生变化。我们知道多个线程同时读写同一共享变量会存在并发问题,如果我们把这里的共享变量改为只读,就不会有并发问题。

我们常用的Integer、Long、Double、String等类都使用了该模式,这些类本身和属性都使用final关键字修饰,使得类不能被继承,属性不能被修改。

当我们对这些类进行修改时,实际上是创建一个新的对象并返回,并不会对旧对象进行修改。

如果我们每次修改都创建新对象的话,是不是有点浪费内存了。针对这点,人们又提出享元模式(Flyweight Pattern)。享元模式可以理解为对象池,对于Long、Integer、Short、Byte等包装类,会预先缓存好常用的数据,之后需要使用到该数据就从缓存中拿。例如Long缓存了数字[-128,127],这个对象池在JVM启动时就会创建好。

若final修饰变量a,说明a的引用不可变,但并不约束引用内容的可变性。例如引用的是对象A,那么对象A的属性是否可变不受该final约束。

Copy on Write模式

在Immutability模式中,对只读对象的修改会创建新的对象,这种方法是写时复制(Copy on Write, Cow)。CoW是解决不可变对象写操作的一个方法。

Copy on Write在许多领域都有广泛的应用,例如Linux中的fork()、Redis中的BGSAVE(本质也是fork())、Java中的CopyOnWriteArrayList等。

CoW的缺点是会消耗较多内存,所以在写操作频繁以及数据量较大时不适合使用。

线程本地存储模式

线程本地存储模式是指不共享数据,每个线程有一份自己的数据备份。例如ThreadLocal和局部变量。

Guarded Suspension模式

Guarded Suspension模式本质上是一种等待唤醒机制的实现,典型的实现就是管程。

该模式与Balking模式都是多线程的if版本,区别是该模式会一直等待if条件变为真。

Balking模式

Balking模式是指在多线程之间共享一个状态变量,业务逻辑依赖于这个状态变量的状态:当状态满足某个条件时,执行某个业务逻辑。在Java中实现Balking模式的关键是可见性,可见性可以通过互斥锁和volatile解决。

双重检查的单例模式就是Balking模式的一个应用。

该模式与Guarded Suspension模式都是多线程的if版本,区别是该模式只会判断一次条件,不会等待。

Thread-Per-Message模式

Thread-Per-Message模式,即为每个任务分配一个独立的线程。Thread-Per-Message模式的一个最经典的应用场景是网络编程里服务端的实现,服务端为每个客户端请求创建一个独立的线程。

Work Thread模式

Worker Thread模式可以类比现实世界里车间的工作模式:车间里的工人,有活儿了,大家一起干,没活儿了就聊聊天等着。Worker Thread对应到现实世界里,其实指的就是车间里的工人。

Java中的线程池采用了这种模式。

两阶段终止模式

两阶段终止模式,即将终止过程分成两个阶段。用于优雅地终止线程,其中第一阶段主要是线程T1向线程T2发送终止指令,第二阶段则是线程T2响应指令。

生产者-消费者模式

  • 解耦
  • 异步
  • 平衡生产者、消费者之间的速度差异