顧名思義,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
方法指定超時時間。