在過去咱們實現多線程同步的代碼中,每每使用join()、wait()、notiyAll()等線程間通訊的方式,隨着JUC包的不斷的完善,java爲咱們提供了豐富同步工具類,官方也鼓勵咱們使用工具類來實現多線程的同步,今天咱們就對其中CountDownLatch類的使用與底層實現進行分析與總結。java
CountDownLatch其實能夠看作一個計數器,統計多個線程執行完成的狀況,適用於控制一個或多個線程等待,直到全部線程都執行完畢的場景,相似與Thread.join()的做用。下面咱們經過一個簡單的例子看下CountDownLatch的使用。安全
public static void main(String[] args) { final CountDownLatch countDownLatch = new CountDownLatch(5); // 啓動計數線程 for (int i = 0; i < 5; i++) { new CountDownLatchThread(i, countDownLatch).start(); } // 啓動等待線程 for (int i = 0; i < 5; i++) { new Thread() { public void run() { try { countDownLatch.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("計數完畢了," + Thread.currentThread().getName() + "等待線程執行"); } }.start(); } }
計數線程代碼:微信
public class CountDownLatchThread extends Thread { private CountDownLatch countDownLatch; private int name; private int count; public CountDownLatchThread(int name, CountDownLatch countDownLatch) { this.name = name; this.countDownLatch = countDownLatch; this.count = 0; } public void run() { try { for (int i = 0; i < 10; i++) { Thread.sleep(100); count++; } System.out.println(name + "號線程--" + Thread.currentThread().getName() + "--計數完成了"); countDownLatch.countDown(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
輸出結果:多線程
1號線程--Thread-1--計數完成了 0號線程--Thread-0--計數完成了 4號線程--Thread-4--計數完成了 2號線程--Thread-2--計數完成了 3號線程--Thread-3--計數完成了 計數完畢了,Thread-5等待線程執行 計數完畢了,Thread-6等待線程執行 計數完畢了,Thread-7等待線程執行 計數完畢了,Thread-8等待線程執行 計數完畢了,Thread-9等待線程執行
經過上面的例子能夠看到,利用CountDownLatch的countDown方法與await()方法,咱們能夠同步計數線程與等待線程,使等待線程在全部計數線程完成後再開始運行。函數
接下來咱們對countDownLatch內部源碼進行一下分析。工具
首先看下CountDownLatch的構造函數源碼分析
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
CountDownLatch的構造函數會接收一個count值作爲計數器,也就是若是你須要等待N個線程執行結束,那這裏就傳入N。同時CountDownLatch會實例化一個Sync對象,這個Sync實際上是CountDownLatch內部定義的一個繼承自AbstractQueuedSynchronizer的實現類,因此CountDownLatch提供的同步和其餘功能都是圍繞Sync這個子類實現的,也就是基於AbstractQueuedSynchronizer類來實現的。ui
咱們來看下Sync這個類的定義this
private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; /** 一、設置AbstractQueuedSynchronizer中同步狀態的值state,也就是計數器的值。 二、這個值volatile變量,必須保證線程間的可見性; **/ Sync(int count) { setState(count); } int getCount() { return getState(); } //獲取同步狀態的值 protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } protected boolean tryReleaseShared(int releases) { // 經過CAS操做改變同步狀態值,保證同步狀態的值state的線程安全。 for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } } }
首先咱們看下countDown方法的源碼spa
public void countDown() { //改變同步狀態值,線程執行完成時計數器減一 sync.releaseShared(1); }
AbstractQueuedSynchronizer類中releaseShared() 方法的源碼
public final boolean releaseShared(int arg) { // CountDownLatch定義的子類Sync實現,經過CAS操做改變State的值 if (tryReleaseShared(arg)) { //State以遞減爲0,表明着全部執行線程執行完畢,共享模式下釋放鎖,那麼等待線程就可以拿到鎖往下執行。 doReleaseShared(); return true; } return false; }
當調用CountDownLatch的countDown方法時,就會執行計數器進行減一操做,直到全部線程所有執行完畢,計算器爲0時喚醒等待線程。
AbstractQueuedSynchronizer中doReleaseShared方法是執行共享模式下釋放鎖的操做,從而讓等待線程獲取鎖,繼續向下執行。
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
AbstractQueuedSynchronizer類中acquireSharedInterruptibly() 方法的源碼
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //獲取同步狀態值 if (tryAcquireShared(arg) < 0) //同步狀態值即計數器的值不爲0,等待線程共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞 doAcquireSharedInterruptibly(arg); }
await方法的實現也很明確,首頁獲取同步狀態也就是計數器的值,若是爲0即全部線程執行完畢返回1,不然返回-1的話,等待線程在共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞;
AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是執行共享模式下獲取鎖的操做;
經過上面分析能夠看到CountDownLatch是基於AbstractQueuedSynchronizer類實現的,一個很是實用的多線程控制工具類,它相似與一個計數器用來控制指定的線程等待,直到計數器歸零。以上咱們對CountDownLatch類的使用與核心方法的源碼進入了必定的分析,其中若有不足與不正確的地方還望指出與海涵。
關注微信公衆號,查看更多技術文章。