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
方法后手動執行一段代碼實現相同的功能,但執行者為主線程。
- 前者在每個子線程上都進行阻塞,然后一起放行;后者僅在主線程上阻塞一次。
- 前者可以重復使用;后者的倒計數器歸零后就作廢了。
- 兩者的內部實現完全不同。