一、 應用場景html
CountDownLatch是併發包中用來控制一個或者多個線程等待其餘線程完成操做的併發工具類。現以工做中的一個場景來描述下CountDownLatch的應用,代碼以下:算法
/*模擬工做中的一個需求場景: 用戶會選擇多個算法來計算費用,最後會將全部算法計算出的費用作一個加權求平均數,這個平均數是最終的費用。 每一個算法的複雜度都不同,打算每一個線程負責一個算法的實現,全部的線程執行完成,最後再求平均數。 一、爲每一個算法建立一個線程,每一個線程負責一個算法的實現 二、經過CountDownLatch來控制全部算法線程的同步 三、所有計算完成後再求平均數 */public class CountDownLatchTask { public static void main(String[] args) { CountDownLatchTask countDownLatchTask = new CountDownLatchTask(); countDownLatchTask.startThreads(5); } //根據線程數和選擇的算法 調度算法對應的實現 private void startThreads(int threadNumber) { CountDownLatch countDownLatch = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println("線程算法實現:" + Thread.currentThread().getName()); countDownLatch.countDown(); } }).start(); } try { countDownLatch.await(); System.out.println("加權求平均數"); } catch (InterruptedException e) { e.printStackTrace(); } } }
在分析原理實現前,總結下CountDownLatch的做用就是阻塞其餘線程直到條件容許後才釋放該阻塞,除了上述這個小案例,實際工做中還有不少可使用CountDownLatch的場景,好比解析Excel文件時能夠同時解析多個Sheet頁,全部的Sheet解析完成纔算完成了Excel文件的解析。從這個代碼中也能夠看到CountDownLatch的主要方法就是await和countDown,下面將以這兩個方法來分析下CountDownLatch的原理實現。併發
二、 源碼原理解析ide
2.1 await方法工具
調用await方法會阻塞當前線程直到計數器的數值爲0,方法以下:學習
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); //共享式獲取AQS的同步狀態}
調用的是AQS的acquireSharedInterruptibly方法:ui
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted())//線程中斷 說明閉鎖對線程中斷敏感 throw new InterruptedException(); if (tryAcquireShared(arg) < 0) //閉鎖未使用完成 線程進入同步隊列自旋等待 doAcquireSharedInterruptibly(arg); }
其中tryAcquireShared依賴的是Sync的實現,和以前的ReentrantLock、ReentrantReadWriteLock及Semaphore相比,CountDownLatch的Sync只提供了一種方式,代碼以下:線程
protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; //AQS的同步狀態爲0則閉鎖結束 能夠進行下一步操做 }
doAcquireSharedInterruptibly方法就再也不贅述,和以前Semaphore的實現是一致的,本質上仍然是AQS同步隊列的入隊自旋等待。htm
2.2 countDown方法blog
調用countDown方法會將計數器的數值減1直到計數器爲0,方法以下:
public void countDown() { sync.releaseShared(1); }
和Semaphore同樣,調用的是AQS的releaseShared方法:
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) {//減小閉鎖的計數器 doReleaseShared();//喚醒後續線程節點 return true; } return false; }
其中tryReleaseShared依賴的是Sync的實現,和以前的ReentrantLock、ReentrantReadWriteLock及Semaphore相比,CountDownLatch的Sync只提供了一種方式,代碼以下:
protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; //計數器已是0了 int nextc = c-1; //計數器減1 if (compareAndSetState(c, nextc)) //CAS更新同步狀態 return nextc == 0; 加羣:874811168 一塊兒學習,一塊兒進步 進羣便可免費領取資料一份 } }
喚醒後續線程節點的doReleaseShared也再也不贅述,和以前Semaphore的實現是一致的。
總結:CountDownLatch類使用AQS同步狀態來表示計數。在await時,全部的線程進入同步隊列自旋等待,在countDown時,獲取閉鎖成功的線程會減小閉鎖的計數器,同時喚醒後續線程取獲取閉鎖,直到await中的計數器爲0,獲取到閉鎖的線程才能夠經過,執行下一步操做。
出處:https://www.cnblogs.com/iou123lg/p/9739697.html