一、CyclicBarrier简介
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 当前线程已经到达了屏障,然后当前线程被阻塞
二、使用案例
使用场景
当存在需要所有的子任务都完成时,才执行主任务,这个
时候就可以选择使用 CyclicBarrier
案例
定义线程,定义每组运行3个,当未达到3个线程的时候阻塞,当达到3个以后会执行
public class Run extends Thread {
private CyclicBarrier cyclicBarrier;
public Run(CyclicBarrier cyclicBarrier){
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("线程"+Thread.currentThread().getName()+"准备就绪");
cyclicBarrier.await();
System.out.println("线程"+Thread.currentThread().getName()+"开始执行");
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
}
}
测试代码,定义3个线程运行
public class CyclicBarrierDemo {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
public static void main(String[] args) {
for(int i=0;i<9;i++) {
new Run(cyclicBarrier).start();
}
}
}
打印效果
线程Thread-2准备就绪
线程Thread-1准备就绪
线程Thread-0准备就绪
线程Thread-0开始执行
线程Thread-2开始执行
线程Thread-1开始执行
Process finished with exit code 0
注意点
1)对于指定计数值 parties,若由于某种原因,没有足够的线程调用 CyclicBarrier 的 await,则所有调用 await 的线程都会被阻塞;
2)同样的 CyclicBarrier 也可以调用 await(timeout, unit),设置超时时间,在设定时间内,如果没有足够线程到达,则解除阻塞状态,继续工作;
3)通过 reset 重置计数,会使得进入 await 的线程出现BrokenBarrierException;
4 ) 如 果 采 用 是 CyclicBarrier(int parties, RunnablebarrierAction) 构造方法,执行 barrierAction 操作的是最后一个到达的线程
例如针对上面的实例,我们调整一下代码
public class CyclicBarrierDemo {
// CyclicBarrier的构造方法可以传入一个回调线程
static CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new ThreadTest());
public static void main(String[] args) {
for(int i=0;i<3;i++) {
new Run(cyclicBarrier).start();
}
}
}
public class ThreadTest extends Thread {
@Override
public void run() {
System.out.println("所有线程准备好,开始执行");
}
}
再执行,打印效果如下
线程Thread-1准备就绪
线程Thread-3准备就绪
线程Thread-2准备就绪
所有线程准备好,开始执行
线程Thread-2开始执行
线程Thread-3开始执行
线程Thread-1开始执行
Process finished with exit code 0
三、源码分析
CyclicBarrier 相比 CountDownLatch 来说,要简单很多,源码实现是基于 ReentrantLock 和 Condition 的组合使用
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
// 记录初始化拦截的线程数
private final int parties;
// 记录当前还需要拦截的线程数,初始化值==parties
private int count;
每次线程调用CyclicBarrier.await()方法,先count-1,然后判断count==0:如果是,则调用Condition.signAll方法唤醒这一组所有线程,并且重新赋值count = parties;
如果否,则调用Condition.await方法阻塞
本文是综合自己的认识和参考各类资料(书本及网上资料)编写,若有侵权请联系作者,所有内容仅代表个人认知观点,如有错误,欢迎校正; 邮箱:1354518382@qq.com 博客地址:https://blog.csdn.net/qq_35576976/