CyclicBarrier JUC包下 相当于可重复使用的CountDownLauth
之前聊过 CountDownLauth类似一种栅栏,条件每满足一次 state-1 ,state减到0时 打开栅栏释放所有阻塞住的线程
CountDownLautch相当于一次性的栅栏,释放过一次后便无用了。而CyclicBarrier 相当于可重复使用的栅栏。
CountDownLautch是基于AQS实现的,而CyclicBarrier是基于Condition实现的
基本使用
new CyclicBarrier( int parties, Runnable barrierAction)
第一个参数是栅栏的条件次数,第二个是栅栏准备释放之前执行的操作(由最后一个线程执行)。
源码分析
CyclicBarrier几个属性
Genetation : 静态内部类 代表栅栏一代版本 内部属性broken代表当前版本是否终结
ReentrantLock : 内部一个锁
Condition : 用来唤醒线程
parties : 初始化的栅栏条件数
Runnable : 栅栏打开之前要执行的操作
count : 当前计数,每await()一次-1
1. CyclicBarrier() 构造方法 没啥好说的
2.await() 每一个线程调用await()时
dowait()
先上锁
try{
获取当前CyclicBarrier版本;
如果版本已经终结,抛异常;
if (线程中断) {
终结当前版本,重置count,唤醒所有线程;
抛中断异常
}
count计数--;
if (如果计数为0 也就是达到了释放栅栏条件 ){
执行Runnable中的操作;
开启下一个CyclicBarrier版本;
if (如果中途执行Runnable操作失败了报错了){
最后也一定会 终结当前版本,重置count,唤醒所有线程;
}
}
}
下边是for循环死循环
for ( ; ; ){
try{
执行condition.await()方法;
catch(中断异常){
如果中途被中断,重置版本,重置count,唤醒所有线程;
}
if (版本终结){
抛异常
}
if (获取的版本 已经不是当前版本){
返回当前计数
}
if(等待超时) {
重置,并抛超时异常;
}
finally{ 解锁}
}
3. nextGeneration() 当栅栏达到条件执行此方法 将condition.await()的线程全部唤醒,重置count,换一个新版本
4. reset() 更简单了 终结现在的版本,全部重置并开始一个新版本
什么时候栅栏会被打破,总结如下:
- 中断,我们说了,如果某个等待的线程发生了中断,那么会打破栅栏,同时抛出 InterruptedException 异常;
- 超时,打破栅栏,同时抛出 TimeoutException 异常;
- 指定执行的操作抛出了异常,这个我们前面也说过。