Java並發和多線程4:使用通用同步工具CountDownLatch實現線程等待
CountDownLatch,一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。
用給定的計數 初始化 CountDownLatch。由於調用了 countDown() 方法,所以在當前計數到達零之前,await 方法會一直受阻塞。
之後,會釋放所有等待的線程,await 的所有後續調用都將立即返回。
這種現象只出現一次——計數無法被重設。如果需要重設計數,請考慮使用 CyclicBarrier。
CountDownLatch 是一個通用同步工具,它有很多用途。
將計數 1 初始化的 CountDownLatch 用作一個簡單的開/關鎖存器,或入口:在通過調用 countDown() 的線程開啟入口前,所有調用 await的線程都一直在入口處等待。
用 N 初始化的 CountDownLatch 可以使一個線程在 N 個線程完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待。
CountDownLatch 的一個有用特性是,它不要求調用 countDown 方法的線程等到計數到達零時才繼續,而在所有線程都能通過之前,它只是阻止任何線程繼續通過一個 await。
用法1: 下面給出了兩個類,其中一組 worker 線程使用了兩個倒計數鎖存器:
// 第一個類是一個啟動訊號,在 driver 為繼續執行 worker 做好準備之前,它會阻止所有的 worker 繼續執行。
// 第二個類是一個完成訊號,它允許 driver 在完成所有 worker 之前一直等待。
class Driver {void start() throws InterruptedException {CountDownLatch startSignal = new CountDownLatch(1);int N = 5;CountDownLatch doneSignal = new CountDownLatch(N);for (int i = 0; i < N; i++) {Worker worker = new Worker(startSignal, doneSignal,i);Thread thread = new Thread(worker);thread.start();}doSomethingElse(1); // 所有線程都還沒有開始執行startSignal.countDown(); // 讓所有線程開始執行doSomethingElse(2);doneSignal.await(); // 等待所有線程結束doSomethingElse(3);}// 執行一些其它的事情,具體結合實際情況private void doSomethingElse(int i) {System.out.println("doSomethingElse-"+i);}}class Worker implements Runnable {private final CountDownLatch startSignal;private final CountDownLatch doneSignal;private final int i;Worker(CountDownLatch startSignal, CountDownLatch doneSignal,int i) {this.startSignal = startSignal;this.doneSignal = doneSignal;this.i=i;}public void run() {try {startSignal.await();doWork(i);doneSignal.countDown();} catch (InterruptedException ex) {ex.printStackTrace();}}void doWork(int i) {System.out.println("doWork-"+i);}}
// 另一種典型用法2,將一個問題分成 N 個部分,用執行每個部分並讓鎖存器倒計數的 Runnable 來描述每個部分,然後將所有 Runnable
// 加入到 Executor 隊列。當所有的子部分完成後,協調線程就能夠通過
// await。(當線程必須用這種方法反覆倒計數時,可改為使用CyclicBarrier。)
class Driver2 {void start() throws InterruptedException {int N = 5;CountDownLatch doneSignal = new CountDownLatch(N);ExecutorService e = Executors.newFixedThreadPool(3);// 建立並執行線程for (int i = 0; i < N; ++i) {WorkerRunnable workerRunnable = new WorkerRunnable(doneSignal, i);e.execute(workerRunnable);}// 等待所有線程結束doneSignal.await();//手動關閉,才會停止所有線程e.shutdown();}}class WorkerRunnable implements Runnable {private final CountDownLatch doneSignal;private final int i;WorkerRunnable(CountDownLatch doneSignal, int i) {this.doneSignal = doneSignal;this.i = i;}public void run() {doWork(i);doneSignal.countDown();}void doWork(int i) {System.out.println("doWork-" + i);}}
運行程式
package cn.fansunion.executorframework;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {//Driver中的若干Thread,執行完畢後,自動就關閉了Driver driver = new Driver();driver.start();//Driver2中,需要手動調用ExecutorService.shutdown關閉線程Driver2 driver2 = new Driver2();driver2.start();}}
控制台結果
doSomethingElse-1
doSomethingElse-2
doWork-0
doWork-2
doWork-4
doWork-3
doWork-1
doSomethingElse-3
doWork-1
doWork-0
doWork-3
doWork-4
doWork-2
更多程式碼範例:
http://git.oschina.net/fansunion/Concurrent(逐步更新中)