写并发程序时, 很多问题前人曾经终结了有模板套的编程模式,遇到类似的问题时套用模式无需从头从新思考实现.防止bug的产生,以下列举了并发编程罕用的一些模式与应用场景
- 不变模式
多线程环境下, 能final的尽量final润饰. 这点能够配合"写时复制"一起了解,如copyOnWriteArray写时并没有改变原先的数组(也就是不可变),
润饰成final并不是就意味着线程平安,因为只是援用不可变,对象属性还是多线程批改的. 同时还要留神 "不平安公布" 的问题(如办法返回爱护对象援用)
- 线程关闭
即共享变量只有持有的线程这一个线程能够拜访,不让共享,也就线程平安了. 这就是线程关闭的含意. 对应于java中,指的就是ThreadLocal
- Guarded Suspension模式
翻译过去能够是"有保障的停止", 总感觉怪怪的,其实说的就是 多线程的 期待告诉模型. 如下异步转同步的场景就用到这种模式
public class DefaultFuture<T> { /** * 后果对象 */ private T obj; private final Lock lock = new ReentrantLock(); private final Condition done = lock.newCondition(); private final int timeout = 2; final static Map<Object, DefaultFuture> fs = new ConcurrentHashMap<>(); /** * 静态方法创立GuardedObject(DefaultFuture) * @param key * @param <K> * @return */ public static <K> DefaultFuture create(K key) { DefaultFuture f = new DefaultFuture(); fs.put(key, f); return f; } public static <K, T> void fireEvent(K key, T obj) { DefaultFuture go = fs.remove(key); if (go != null) { go.onChanged(obj); } } /** * 返回后果对象 */ public T get(Predicate<T> p) { lock.lock(); try { //MESA管程举荐写法 while (!p.test(obj)) { done.await(timeout, TimeUnit.SECONDS); } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } return obj; } public void onChanged(T obj) { lock.lock(); try { this.obj = obj; done.signalAll(); } finally { lock.unlock(); } }}
- COW, 即copy on write 模式
实用于 保护数据量少, 读多写少的场景. 如rpc调用外面的路由信息保护, 元数据保护, 缓存等场景.
留神: 因为写时复制,先copy,copy完后,而后调整援用指向. 故读时会有短暂的不统一. 用到的时候须要思考到场景是否可能容忍这种不统一景象.
- balking 模式
与其说是一种模式,还不如说是对"同步"概念的进一步思考. 如果多线程场景含有"条件依赖"的语义,(如单例初始化,如果没有初始化才初始化)
这就没必要将整个实例化办法加锁. 只须要对实例化办法块加锁. 如下所示相比于对整个getInstance办法加锁,曾经实例化的状况性能会更高.
class Singleton{ private static volatile Singleton singleton; //构造方法私有化 private Singleton() {} //获取实例(单例) public static Singleton getInstance() { //第一次查看 if(singleton==null){ synchronize(Singleton.class){ //获取锁后二次查看 if(singleton==null){ singleton=new Singleton(); } } } return singleton; }}
- Work per Thread
即每个工作一个线程,java中的线程属于特地耗费占资源的对象,用原生的不可行. 轻量级线程(协程)可采纳此模式,java开源我的项目 Loom 我的项目的 Fiber
据说能够实现,有空能够钻研下
- Work-Thread 模式
也就是线程池模式.须要留神的是线程池里的工作每个工作须要各自独立,防止有依赖关系,不然的话有死锁危险.
呈现这种问题的解决办法是,将相互有依赖的线程放在不同的池中执行.
- 两阶段终止协定(thread.interrupt)
java中的中断并不是粗鲁的一刀切中断, 而是通过中断标识符,将中断后的解决逻辑交给须要被中断的线程来解决
- 抛出interruptException后,以后线程的中断标识会被革除. 故捕捉到异样后,须要从新设置异样标识(Thread.currentThread().interrupt())
- 如果解决中断的线程中援用了第三方包,可能第三方包没有正确处理中断异样(如未从新设置中断标识),这种状况下最好本人新建一个
中断状态,线程的中断依赖于本人建的这个状态,而非线程自带的.
- 生产者-消费者模式.
也是最多使用的一种多线程协同模式,用的最多, 不做开展