一:风骚概述
LOL进入游戏等待,很多召唤师都喜欢说你家用的小霸王吧?等待最慢一个召唤师加载100%后再开始展开召唤师峡谷的剧情,保证游戏公平。其实对于倒数计数器CountDownLatch与栅栏CyclicBarrier的区别个人认为仁者见仁智者见智,场景有重叠与特例,只要掌握其核心实现原理与功能,才能选择最佳的实现方式
二:重要属性
- lock:独占锁保障线程安全
- trip:condition实例睡眠线程等待
- parties:栅栏阻碍线程数量
- barrierCommand:开启栅栏后执行的线程
- generation:描述栅栏是否被破坏
三:栅栏厚度
CyclicBarrier作用就是等待N数量线程后唤醒所有等待线程开始执行,那么这个N的数量级相关设定就显得尤为重要
3.1 初始化赋值
实例化CyclicBarrier对象时会传参parties指定栅栏的厚度,第二个参数是后续会讲到的当栅栏开启后会执行的线程。构造函数还有一个仅仅传入参数parties数量的重载,该构造函数调用上图所示构造,参数barrierAction默认为null
3.2 更新赋值
栅栏CyclicBarrier与倒数计数器CountDownLatch有一个重要的区别就是栅栏可以重复使用,其实现就是原理就是重置栅栏厚度。通过前面的构造函数还可以得到一个信息就是栅栏的厚度会赋值给count,该属性才是记录当前运行时栅栏厚度,而栅栏初始赋值参数使用parties保存
通过reset()就会重置栅栏厚度,但是这时候中间过程会唤醒所有栅栏等待线程,且栅栏状态为破损,有可能会抛出BrokenBarrierException异常,需要做必要的异常处理
四:核心方法
CyclicBarrier栅栏的核心方法就是await(),这个方法中包含栅栏状态检测、栅栏厚度检测、线程睡眠等待、等待线程唤醒、栅栏重置等操作
4.1 栅栏状态检测
静态内部类Generation维护布尔值broken标记栅栏是否被破坏,当发生InterruptedException异常或是重置栅栏的时候都会将该状态设置为true,这时候若还有线程调用await()则会抛出BrokenBarrierException异常
4.2 栅栏厚度检测
在这个dowait()片段中可以看到栅栏厚度判断是通过count属性,并且当栅栏厚度为0的时候就会开始执行barrierCommand线程,也就是前面讲到实例化入参的barrierAction线程参数
4.3 线程等待与唤醒
死循环让线程睡眠,通过condition实例trip实现。注意await()方法具备重载,也就是可以指定在栅栏面前等待的时间。注意一点就是当线程等待时间超过设定值的时候会抛出TimeoutException异常,注意异常处理
栅栏厚度判断为0的时候会调用方法nextGeneration(),方法中唤醒所有condition睡眠线程,同时恢复栅栏状态和厚度
4.4 栅栏受损
栅栏受损即抛出标记检测异常InterruptedException等的时候会调用方法brekBarrier(),该方法同样会唤醒所有condition睡眠线程,但是注意这里会将栅栏状态设置为破损。所以其它线程执行注意处理BrokenBarrierException异常