CountDownLatch是一個同步輔助類,它容許一個或多個線程一直等待直到其餘線程執行完畢纔開始執行。java
用給定的計數初始化CountDownLatch,其含義是要被等待執行完的線程個數。dom
每次調用CountDown(),計數減1ide
主程序執行到await()函數會阻塞等待線程的執行,直到計數爲0函數
計數器經過使用鎖(共享鎖、排它鎖)實現this
場景:模擬10人賽跑。10人跑完後才喊"Game Over."spa
package com.jihite; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownLatchTest { private static final int RUNNER_COUNT = 10; public static void main(String[] args) throws InterruptedException { final CountDownLatch begin = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(RUNNER_COUNT); final ExecutorService exec = Executors.newFixedThreadPool(10); for (int i = 0; i < RUNNER_COUNT; i++) { final int NO = i + 1; Runnable run = new Runnable() { @Override public void run() { try { begin.await(); Thread.sleep((long)(Math.random() * 10000)); System.out.println("No." + NO + " arrived"); } catch (InterruptedException e) { e.printStackTrace(); } finally { end.countDown(); } } }; exec.submit(run); } System.out.println("Game Start ..."); begin.countDown(); end.await(); // end.await(30, TimeUnit.SECONDS); System.out.println("Game Over."); exec.shutdown(); } }
分析:代碼中定義了2個計數器,個數分別爲1和10。線程
若是不執行begin.countDown(),進程會一致阻塞在begin.await()進程
主進程執行到end.awit()阻塞等待end計數器清0,進程中每執行一次CountDown()減1,全部執行完後主進程繼續往下執行get
輸出同步
Game Start ... No.6 arrived No.4 arrived No.10 arrived No.3 arrived No.9 arrived No.5 arrived No.8 arrived No.7 arrived No.1 arrived No.2 arrived Game Over.
注:countDown()必定要執行到(考慮異常及線程與開始計數設置不一致),不然會一直卡在await()(能夠設置時間,超過必定時間就不等了)
場景:流水線上有3個worker: worker一、worker二、worker3,只有當worker1和worker2執行完時才能夠執行worker3
WorkerCount.java
package com.jihite; import java.util.concurrent.CountDownLatch; public class WorkerCount extends Thread { private String name; private long time; private CountDownLatch countDownLatch; public WorkerCount(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } @Override public void run() { try { System.out.println(name + "開始工做"); Thread.sleep(time); System.out.println(name + "工做完成, 耗時:"+ time); countDownLatch.countDown(); System.out.println("countDownLatch.getCount():" + countDownLatch.getCount()); } catch (InterruptedException e) { e.printStackTrace(); } } }
CountDownLatch實現:
@Test public void CountDownLatchTest() throws InterruptedException { int COUNT = 2; final CountDownLatch countDownLatch = new CountDownLatch(COUNT); WorkerCount worker0 = new WorkerCount("lilei-0", (long)(Math.random() * 10000), countDownLatch); WorkerCount worker1 = new WorkerCount("lilei-1", (long)(Math.random() * 10000), countDownLatch); worker0.start(); worker1.start(); countDownLatch.await(); System.out.println("準備工做就緒"); WorkerCount worker2 = new WorkerCount("lilei-2", (long)(Math.random() * 10000), countDownLatch); worker2.start(); Thread.sleep(10000); }
輸出:
lilei-0開始工做 lilei-1開始工做 lilei-1工做完成, 耗時:4039 countDownLatch.getCount():1 lilei-0工做完成, 耗時:9933 countDownLatch.getCount():0 準備工做就緒 lilei-2開始工做 lilei-2工做完成, 耗時:6402 countDownLatch.getCount():0
該場景join也能夠完成
Worker.java
package com.jihite; public class Worker extends Thread{ private String name; private long time; public Worker(String name, long time) { this.name = name; this.time = time; } @Override public void run() { try { System.out.println(name + "開始工做"); Thread.sleep(time); System.out.println(name + "工做完成, 耗時:"+ time); } catch (InterruptedException e) { e.printStackTrace(); } } }
join實現
@Test public void JoinTest() throws InterruptedException { Worker worker0 = new Worker("lilei-0", (long)(Math.random() * 10000)); Worker worker1 = new Worker("lilei-1", (long)(Math.random() * 10000)); Worker worker2 = new Worker("lilei-2", (long)(Math.random() * 10000)); worker0.start(); worker1.start(); worker0.join(); worker1.join(); System.out.println("準備工做就緒"); worker2.start(); Thread.sleep(10000); }
輸出
lilei-0開始工做 lilei-1開始工做 lilei-1工做完成, 耗時:4483 lilei-0工做完成, 耗時:6301 準備工做就緒 lilei-2開始工做 lilei-2工做完成, 耗時:6126
既然這樣,那CountDownLatch和join的區別在哪?經過下面的場景三就能夠看出
場景:流水線上有3個worker: worker一、worker二、worker3,只有當worker1和worker2二者的階段一都執行完後才能夠執行worker3
WorkerCount2.java
package com.jihite; import java.util.concurrent.CountDownLatch; public class WorkerCount2 extends Thread { private String name; private long time; private CountDownLatch countDownLatch; public WorkerCount2(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } @Override public void run() { try { System.out.println(name + "開始階段1工做"); Thread.sleep(time); System.out.println(name + "階段1完成, 耗時:"+ time); countDownLatch.countDown(); System.out.println(name + "開始階段2工做"); Thread.sleep(time); System.out.println(name + "階段2完成, 耗時:"+ time); } catch (InterruptedException e) { e.printStackTrace(); } } }
此時用join沒法實現,只能用CountDownLatch
@Test public void CountDownLatchTest2() throws InterruptedException { int COUNT = 2; final CountDownLatch countDownLatch = new CountDownLatch(COUNT); WorkerCount2 worker0 = new WorkerCount2("lilei-0", (long)(Math.random() * 10000), countDownLatch); WorkerCount2 worker1 = new WorkerCount2("lilei-1", (long)(Math.random() * 10000), countDownLatch); worker0.start(); worker1.start(); countDownLatch.await(); System.out.println("準備工做就緒"); WorkerCount2 worker2 = new WorkerCount2("lilei-2", (long)(Math.random() * 10000), countDownLatch); worker2.start(); Thread.sleep(10000); }
輸出
lilei-0開始階段1工做 lilei-1開始階段1工做 lilei-0階段1完成, 耗時:3938 lilei-0開始階段2工做 lilei-1階段1完成, 耗時:6259 lilei-1開始階段2工做 準備工做就緒 lilei-2開始階段1工做 lilei-0階段2完成, 耗時:3938 lilei-1階段2完成, 耗時:6259 lilei-2階段1完成, 耗時:7775 lilei-2開始階段2工做