Java并发之CyclicBarrier
定义
CyclicBarrier译为回环栅栏,是J.U.C里面提供的工具,字面意思是可循环使用的屏障,作用是可以让一组线程同时到达某个屏障被阻塞,直到最后一个线程到达屏障之后再全部开始执行,和常见的CountDownLatch,Semaphore不一样的是,CyclicBarrier支持重用。
方法
默认构造函数
public CyclicBarrier(int parties)
- parties: 屏障拦截的线程数量
另一个构造函数
public CyclicBarrier(int parties, Runnable barrierAction)
这个构造函数会在线程到达屏障时,优先执行barrierAction,便于实现更复杂的业务场景。
应用场景
举一个比较生动的例子,运动会中跑步项目,把每个运动员看作一个线程,开始跑之前,就需要等每个运动会准备完成之后,才能同时起跑,代码如下:
public class MyThread implements Runnable {
private String name;
private CyclicBarrier cb;
public MyThread(String name, CyclicBarrier cb) {
this.name = name;
this.cb = cb;
}
@Override
public void run() {
try {
System.out.println(this.name + " 开始准备");
Thread.sleep(1000 * 3);
System.out.println(this.name + " 准备完成");
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(this.name + " 起跑");
}
}
使用线程池创建n个线程进行测试
public static void main(String[] args) {
int n = 5;
CyclicBarrier cb = new CyclicBarrier(n, () -> System.out.println("运动会准备完毕,开始起跑!"));
ExecutorService executorService = Executors.newFixedThreadPool(n);
try {
for (int i = 1; i <= n; i++) {
executorService.execute(new MyThread(i + "号选手", cb));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
运行结果如下:
1号选手 开始准备
2号选手 开始准备
3号选手 开始准备
4号选手 开始准备
5号选手 开始准备
2号选手 准备完成
1号选手 准备完成
5号选手 准备完成
4号选手 准备完成
3号选手 准备完成
运动会准备完毕,开始起跑!
说明
这里用到的就是第二个构造函数,当n个线程同时到达屏障(运动员准备完成)时,才开始执行接下来的操作(起跑)。我在写的时候犯了个错误,设置了n=5
,但是创建线程池时却把Executors.newFixedThreadPool(4)
参数写死成了4,这就导致一直没有第五个线程去执行await()
方法,这就导致了其他线程会一直等待,程序不会结束。
总结
CyclicBarrier还有很多应用场景,比如需要多线程处理数据时,需要每个线程处理完数据之后进行汇总,更多的场景需要在工作中慢慢发掘。
参考
- 《Java并发编程的艺术》