顧名思義,CountDownLatch
是一個用來倒計數的咚咚。如果某項任務可以拆分成若干個子任務同時進行,然后等待所有的子任務完成,可以考慮使用它。
該類的用法非常簡單。首先構造一個 CountDownLatch
,唯一的參數是任務數量,一旦構造完畢就不能修改。接著啟動所有的子任務(線程),且每個子任務在完成自己的計算后,調用 CountDownLatch#countDown
方法將倒計數減一。最后在主線程中調用 CountDownLatch#await
方法等待計數器歸零。
例如賽跑的準備階段,八名運動員先后到達起點做好準備,然后裁判打響發令槍,準備工作就結束了,比賽開始。如果把從運動員就位到發令槍響看做賽跑準備任務,那么每個運動員的準備過程就是其子任務,可以用 CountDownLatch
模擬如下:
final int count = 8;
System.out.println("運動員開始就位。");
// 構造 CountDownLatch。
final CountDownLatch cdl = new CountDownLatch(count);
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 + " 號運動員就位。");
// 倒計數減一。
cdl.countDown();
}
}.start();
}
System.out.println("等待所有運動員就位...");
try {
// 等待倒計數變為 0。
cdl.await();
System.out.println("比賽開始。");
} catch (InterruptedException ex) {
}
運行輸出(可能)為:
運動員開始就位。
1 號運動員到場并開始準備...
2 號運動員到場并開始準備...
4 號運動員到場并開始準備...
等待所有運動員就位...
8 號運動員到場并開始準備...
6 號運動員到場并開始準備...
3 號運動員到場并開始準備...
7 號運動員到場并開始準備...
5 號運動員到場并開始準備...
6 號運動員就位。
1 號運動員就位。
5 號運動員就位。
4 號運動員就位。
7 號運動員就位。
8 號運動員就位。
2 號運動員就位。
3 號運動員就位。
比賽開始。
從上面的例子還可以看出 CountDownLatch
的局限性和 CompletionService
類似,在于無法處理子任務數量不確定的情況,例如統計某個文件夾中的文件數量。另外,如果某個子任務在調用 countDown
之前就掛掉了,倒計數就永遠不會歸零。對于這種情況,要么用 finally
之類的手段保證 countDown
一定會被調用,要么用帶參數的 await
方法指定超時時間。