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