CountDownLatch主要用於同步一個或多個任務,強制它們等待由其餘任務執行的一組操做完成。html
你能夠向CountDownLatch對象設置一個初始計數值,任何在這個對象上調用await()的方法都將阻塞,直到這個計數值達到0。其餘任務在結束其工做時,能夠在該對象上調用countDown()來減少這個計數值,你能夠經過調用getCount()方法來獲取當前的計數值。CountDownLatch被設計爲只觸發一次,計數值不能被重置。若是你須要可以重置計數值的版本,則可使用CyclicBarrier。java
調用countDown()的任務在產生這個調用時並無阻塞,只有對await()的調用會被阻塞,直到計數值到達0。併發
CountDownLatch的典型用法是將一個程序分爲n個互相獨立的可解決任務,並建立值爲0的CountDownLatch。當每一個任務完成時,都會在這個鎖存器上調用countDown()。等待問題被解決的任務在這個鎖存器上調用await(),將它們本身鎖住,直到鎖存器計數結束。下面是演示這種技術的一個框架示例:框架
import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.DelayQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class TaskPortion implements Runnable { private static int counter = 0; private final int id = counter++; private static Random random = new Random(); private final CountDownLatch latch; public TaskPortion(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { doWork(); latch.countDown();//普通任務執行完後,調用countDown()方法,減小count的值 System.out.println(this + " completed. count=" + latch.getCount()); } catch (InterruptedException e) { } } public void doWork() throws InterruptedException { TimeUnit.MILLISECONDS.sleep(random.nextInt(2000)); } @Override public String toString() { return String.format("%1$-2d ", id); } } class WaitingTask implements Runnable { private static int counter = 0; private final int id = counter++; private final CountDownLatch latch; public WaitingTask(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { //這些後續任務須要等到以前的任務都執行完成後才能執行,即count=0時 latch.await(); System.out.println("Latch barrier passed for " + this); } catch (InterruptedException e) { System.out.println(this + " interrupted."); } } @Override public String toString() { return String.format("WaitingTask %1$-2d ", id); } } public class CountDownLatchDemo { static final int SIZE = 10; public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(SIZE); ExecutorService exec = Executors.newCachedThreadPool(); //5個WaitingTask for (int i = 0; i < 5; i++) { exec.execute(new WaitingTask(latch)); } //10個任務,這10個任務要先執行纔會執行WaitingTask for (int i = 0; i < SIZE; i++) { exec.execute(new TaskPortion(latch)); } System.out.println("Launched all tasks."); exec.shutdown();//當全部的任務都結束時,關閉exec } }
執行結果(可能的結果):dom
Launched all tasks. 4 completed. count=9 6 completed. count=8 3 completed. count=7 0 completed. count=6 2 completed. count=5 1 completed. count=4 5 completed. count=3 7 completed. count=2 9 completed. count=1 8 completed. count=0 Latch barrier passed for WaitingTask 0 Latch barrier passed for WaitingTask 2 Latch barrier passed for WaitingTask 1 Latch barrier passed for WaitingTask 3 Latch barrier passed for WaitingTask 4
從結果中能夠看到,全部的WaitingTask都是在全部的TaskPortion執行完成以後執行的。ide
TaskPortion將隨機的休眠一段時間,以模擬這部分工做的完成。而WaitingTask表示系統中必須等待的部分,它要等到問題的初始部分完成後才能執行。注意:全部任務都使用了在main()中定義的同一個CountDownLatch對象。this
一個更實際的例子(參考自這裏,有改動):N個選手比賽跑步,在槍響後同時起跑,所有到達終點後比賽結束,下面是代碼:線程
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 用CountDownLatch來模擬運動員賽跑的Demmo. */ public class CountDownLatchDemo { /** 參與比賽的人數(併發線程數) */ private static int PLAYER_NUM = 8; public static void main(String[] args) throws Exception { //用於讓主線程(裁判)等待全部子線程(運動員)就緒 final CountDownLatch readySignal = new CountDownLatch(PLAYER_NUM); //用於讓子線程(運動員)等待主線程(裁判)發號施令 final CountDownLatch beginSignal = new CountDownLatch(1); //用於讓主線程(裁判)等待全部子線程(運動員)執行(比賽)完成 final CountDownLatch endSignal = new CountDownLatch(PLAYER_NUM); ExecutorService executorService = Executors.newFixedThreadPool(PLAYER_NUM); for(int i = 0; i < PLAYER_NUM; i++){ final int num = i + 1; Runnable player = new Runnable(){ @Override public void run() { System.out.println("No." + num + " is ready"); //一個任務就緒後,減小readySignal上的等待 readySignal.countDown(); try { //等待主線程比賽開始的命令 beginSignal.await(); System.out.println("No." + num + " is running"); Thread.sleep((long) (Math.random() * 5000)); System.out.println("No.-----" + num + " arrived"); } catch (InterruptedException e) { e.printStackTrace(); } finally { //每一個線程執行完成後,調用countDown減小在endSignal上的等待線程數 endSignal.countDown(); } } }; executorService.execute(player); } //這裏讓主線程等待,確保全部子線程已開始執行並調用了await進入等待狀態 readySignal.await(); System.out.println("Game Start!"); //全部等待在beginSignal上的線程(運動員)開始執行(比賽) beginSignal.countDown(); try { //等待全部在endSignal上的線程(運動員)執行(比賽)完成 endSignal.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("Game Over!"); executorService.shutdown(); } } }
代碼中用到了3個CountDownLatch對象,具體做用已在代碼中說明,這裏不贅述。設計
說一下執行過程:code
下面是執行結果:
No.1 is ready No.2 is ready No.3 is ready No.4 is ready No.6 is ready No.5 is ready No.7 is ready No.8 is ready Game Start! No.1 is running No.4 is running No.3 is running No.6 is running No.2 is running No.5 is running No.7 is running No.8 is running No.-----3 arrived No.-----2 arrived No.-----1 arrived No.-----8 arrived No.-----4 arrived No.-----6 arrived No.-----7 arrived No.-----5 arrived Game Over!