CountDownLatch是一個同步工具,它主要用線程執行之間的協做。CountDownLatch 的做用和 Thread.join() 方法相似,讓一些線程阻塞直到另外一些線程完成一系列操做後才被喚醒。在直接建立線程的年代(Java 5.0 以前),咱們能夠使用 Thread.join()。在線程池出現後,由於線程池中的線程不能直接被引用,因此就必須使用 CountDownLatch 了。html
CountDownLatch主要有兩個方法,當一個或多個線程調用await方法時,這些線程會阻塞。其它線程調用countDown方法會將計數器減1(調用countDown方法的線程不會阻塞),當計數器的值變爲0時,因await方法阻塞的線程會被喚醒,繼續執行。java
實現原理:計數器的值由構造函數傳入,並用它初始化AQS的state值。當線程調用await方法時會檢查state的值是否爲0,若是是就直接返回(即不會阻塞);若是不是,將表示該節點的線程入列,而後將自身阻塞。當其它線程調用countDown方法會將計數器減1,而後判斷計數器的值是否爲0,當它爲0時,會喚醒隊列中的第一個節點,因爲CountDownLatch使用了AQS的共享模式,因此第一個節點被喚醒後又會喚醒第二個節點,以此類推,使得全部因await方法阻塞的線程都能被喚醒而繼續執行。dom
從源代碼和實現原理中能夠看出一個CountDownLatch對象,只能使用一次,不能重複使用。ide
await方法源碼函數
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
doAcquireSharedInterruptibly 主要實現線程的入列與阻塞。工具
countDown方法ui
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } }
doReleaseShared主要實現喚醒第一個節點,第一個節點有會喚醒第二個節點,……。this
package demo; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownLatchDemo { private CountDownLatch cdl = new CountDownLatch(2); private Random rnd = new Random(); class FirstTask implements Runnable{ private String id; public FirstTask(String id){ this.id = id; } @Override public void run(){ System.out.println("Thread "+ id + " is start"); try { Thread.sleep(rnd.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread "+ id + " is over"); cdl.countDown(); } } class SecondTask implements Runnable{ private String id; public SecondTask(String id){ this.id = id; } @Override public void run(){ try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("----------Thread "+ id + " is start"); try { Thread.sleep(rnd.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("----------Thread "+ id + " is over"); } } public static void main(String[] args){ ExecutorService es = Executors.newCachedThreadPool(); CountDownLatchDemo cdld = new CountDownLatchDemo(); es.submit(cdld.new SecondTask("c")); es.submit(cdld.new SecondTask("d")); es.submit(cdld.new FirstTask("a")); es.submit(cdld.new FirstTask("b")); es.shutdown(); } }
運行結果spa
Thread a is start線程
Thread b is start
Thread b is over
Thread a is over
----------Thread c is start
----------Thread d is start
----------Thread d is over
----------Thread c is over
[1] http://developer.51cto.com/art/201403/432095.htm