CyclicBarrier
的功能类似于前面说到的 CountDownLatch
,用于让多个线程(子任务)互相等待,直到共同到达公共屏障点(common barrier point),在这个点上,所有的子任务都已完成,从而主任务完成。
该类构造的时候除了必须要指定线程数量,还可以传入一个 Runnable
对象,它的 run
方法将在到达公共屏障点后执行一次。子线程完成计算后,分别调用 CyclicBarrier#await
方法进入阻塞状态,直到其他所有子线程都调用了 await
。
下面仍然以运动员准备赛跑为例来说明 CyclicBarrier
的用法:
final int count = 8;
System.out.println("运动员开始就位。");
final CyclicBarrier cb = new CyclicBarrier(count, new Runnable() {
@Override
public void run() {
System.out.println("比赛开始...");
}
});
for (int i = 1; i <= count; i++) {
final int number = i;
new Thread() {
@Override
public void run() {
System.out.println(number + " 号运动员到场并开始准备...");
try {
// 准备 2~5 秒钟。
TimeUnit.SECONDS.sleep(new Random().nextInt(4) + 2);
} catch (InterruptedException ex) {
}
System.out.println(number + " 号运动员就位。");
try {
cb.await();
} catch (InterruptedException | BrokenBarrierException ex) {
}
}
}.start();
}
System.out.println("等待所有运动员就位...");
运行输出(可能)为:
运动员开始就位。
1 号运动员到场并开始准备...
2 号运动员到场并开始准备...
等待所有运动员就位...
3 号运动员到场并开始准备...
4 号运动员到场并开始准备...
6 号运动员到场并开始准备...
8 号运动员到场并开始准备...
5 号运动员到场并开始准备...
7 号运动员到场并开始准备...
1 号运动员就位。
3 号运动员就位。
8 号运动员就位。
6 号运动员就位。
2 号运动员就位。
7 号运动员就位。
5 号运动员就位。
4 号运动员就位。
比赛开始...
最后看看 CyclicBarrier
和 CountDownLatch
的主要异同:
- 两者在构造的时候都必须指定线程数量,而且该数量在构造后不可修改。
- 前者可以传入一个
Runnable
对象,在任务完成后自动调用,执行者为某个子线程;后者可在 await
方法后手动执行一段代码实现相同的功能,但执行者为主线程。
- 前者在每个子线程上都进行阻塞,然后一起放行;后者仅在主线程上阻塞一次。
- 前者可以重复使用;后者的倒计数器归零后就作废了。
- 两者的内部实现完全不同。