田徑賽百米短跑時,運動員會在起跑線做準備動作,等到發令槍一聲響,運動員就會奮力奔跑。在多線程運行時,也有這麼一個發令槍–CountDownLatch,它通過控制事先定義的計數來控制線程的運行。
CountDownLatch的構造方法如下:
[java] view plaincopy
CountDownLatch(int count); //構造一個用給定計數初始化的 CountDownLatch。
主要用到的方法有:
[java] view plaincopy
void await(); //使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷。
boolean await(long timeout, TimeUnit unit); //使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷或超出瞭指定的等待時間。
void countDown(); //遞減鎖存器的計數,如果計數到達零,則釋放所有等待的線程。
long getCount(); // 返回當前計數。
String toString(); //返回標識此鎖存器及其狀態的字符串。
CountDownLatch是一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。對於給定的計數 初始化 CountDownLatch,可以調用瞭 countDown() 方法,在當前計數到達零之前,await() 方法會一直受阻塞,當計數到達零時,會釋放所有等待的線程,await() 的所有後續調用都將立即返回。但這種現象隻出現一次——計數無法被重置,如果需要重置計數,請考慮使用 CyclicBarrier。
CountDownLatch 是一個通用同步工具,它有很多用途,將計數 初始化為1的 CountDownLatch可 用作一個簡單的開/關鎖存器或入口,在通過調用 countDown() 的線程打開入口前,所有調用 await() 的線程都一直在入口處等待。用 N 初始化的 CountDownLatch 可以使一個線程在 N 個線程完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待。
CountDownLatch 的一個有用特性是,它不要求調用 countDown() 方法的線程等到計數到達零時才繼續,而在所有線程都能通過之前,它隻是阻止任何線程繼續通過await(),它比CyclicBarrier有更大的靈活性,它可以控制不確定數目的線程,而不是像CyclicBarrier在確定數目的線程wait()時就會通過,隻有當countDown()的值為0時才允許所有的線程通過。
下面通過一個例子加以說明:
[java]
public class CountdownLatchTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
System.out.println("線程" + Thread.currentThread().getName() +
"正準備接受命令");
cdOrder.await();
System.out.println("線程" + Thread.currentThread().getName() +
"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"回應命令處理結果");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"即將發佈命令");
cdOrder.countDown();
System.out.println("線程" + Thread.currentThread().getName() +
"已發送命令,正在等待結果");
cdAnswer.await();
System.out.println("線程" + Thread.currentThread().getName() +
"已收到所有響應結果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();
}
}
上例中main方法表示主線程,並且通過for循環創建3個子線程,定義瞭兩個CountDownLatch對象:cdOrder和cdAnswer,cdOrder計數為1,用來控制3個子線程,cdAnswer計數為3,用來控制主線程。當程序開始運行時,主線程和子線程同時開始運行,由於主線程需要sleep一段時間,所以3個子線程運行,但是碰到cdOrder.await();必須等到主線程cdOrder.countDown();將計數變成0時才可以繼續往下運行,主線程運行到cdAnswer.await();時等待,隻有當三個子線程都cdAnswer.countDown();將計數變為0時主線程才可以往下運行。改程序運行結果如下:
[java]
線程pool-1-thread-2正準備接受命令
線程pool-1-thread-3正準備接受命令
線程pool-1-thread-1正準備接受命令
線程main即將發佈命令
線程main已發送命令,正在等待結果
線程pool-1-thread-3已接受命令
線程pool-1-thread-2已接受命令
線程pool-1-thread-1已接受命令
線程pool-1-thread-1回應命令處理結果
線程pool-1-thread-3回應命令處理結果
線程pool-1-thread-2回應命令處理結果
線程main已收到所有響應結果