CountDownLatch是具備synchronized機制的一個工具,目的是讓一個或者多個線程等待,直到其餘線程的一系列操做完成。java
CountDownLatch初始化的時候,須要提供一個整形數字,數字表明着線程須要調用countDown()方法的次數,當計數爲0時,線程纔會繼續執行await()方法後的其餘內容。
CountDownLatch(int count);數據庫
getCount: 返回當前的計數count值,
public void countDown() 調用此方法後,會減小計數count的值。 遞減後若是爲0,則會釋放全部等待的線程
public void await() throws InterruptedException 調用CountDownLatch對象的await方法後。 會讓當前線程阻塞,直到計數count遞減至0。
若是當前線程數大於0,則當前線程在線程調度中將變得不可用,並處於休眠狀態,直到發生如下兩種狀況之一:segmentfault
一、調用countDown()方法,將計數count遞減至0。安全
二、當前線程被其餘線程打斷。框架
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
同時await還提供一個帶參數和返回值的方法。ide
若是計數count正常遞減,返回0後,await方法會返回true並繼續執行後續邏輯。函數
或是,還沒有遞減到0,而到達了指定的時間間隔後,方法返回false。工具
若是時間小於等於0,則此方法不執行等待。學習
首先創建3個線程。ui
public class Worker1 implements Runnable { @Override public void run() { System.out.println("-線程1啓動"); try { Thread.sleep(13_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程1完成--我休眠13秒\r\n"); } } public class Worker2 implements Runnable { @Override public void run() { System.out.println("-線程2啓動"); try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程2完成--我休眠3秒\r\n"); } } public class Worker3 implements Runnable { @Override public void run() { System.out.println("-線程3啓動"); try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程3完成--我休眠6秒\r\n"); System.out.println(); } } public class Main { public static void main(String[] args) throws InterruptedException { Worker1 worker1 = new Worker1(); Worker2 worker2 = new Worker2(); Worker3 worker3 = new Worker3(); Thread thread1 = new Thread(worker1,"線程1"); Thread thread2 = new Thread(worker2,"線程2"); Thread thread3 = new Thread(worker3,"線程3"); thread1.start(); thread2.start(); thread3.start(); thread1.join(); thread2.join(); thread3.join(); System.out.println("主線程結束...."); } }
打印結果以下:
-線程3啓動 -線程2啓動 -線程1啓動 線程2完成--我休眠3秒 線程3完成--我休眠6秒 線程1完成--我休眠13秒 主線程結束.... Process finished with exit code 0
能夠看出三個線程是並行執行的。啓動順序,並不和執行完畢的順序一致,但能夠明確的是,主線程爲一直阻塞,直到三個線程執行完畢。
阿里巴巴的數據庫鏈接池Druid中也用了countDownLatch來保證初始化。
// 開啓建立鏈接的線程,若是線程池createScheduler爲null, //則開啓單個建立鏈接的線程 createAndStartCreatorThread(); // 開啓銷燬過時鏈接的線程 createAndStartDestroyThread();
本身編寫一個例子:
這裏模擬一種狀況:
主線程 依賴 線程A初始化三個數據,才能繼續加載後續邏輯。
public class CountDownArticle { /** * 模擬 主線程 依賴 線程A初始化一個數據,才能繼續加載後續邏輯 */ public static void main(String[] args) throws InterruptedException { AtomicReference<String> key = new AtomicReference<>(""); CountDownLatch countDownLatch = new CountDownLatch(3); Thread t = new Thread(() -> { try { //休眠5秒,模擬數據的初始化 TimeUnit.SECONDS.sleep(5); key.set("核心祕鑰123456"); System.out.println("數據1初始化完畢"); //釋放---此處能夠在任何位置調用,很靈活 countDownLatch.countDown(); System.out.println("數據2初始化完畢"); countDownLatch.countDown(); System.out.println("數據3初始化完畢"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }); t.start(); //等待數據初始化,阻塞 countDownLatch.await(); System.out.println("key:" + key.get()); } }
打印內容以下:
數據1初始化完畢 數據2初始化完畢 數據3初始化完畢 key:核心祕鑰123456
在使用join()中,多個線程只有在執行完畢以後歐才能被解除阻塞,而在CountDownLatch中,線程能夠在任什麼時候候任何位置調用countdown方法減小計數,經過這種方式,咱們能夠更好地控制線程的解除阻塞,而不是僅僅依賴於鏈接線程的完成。
join()方法的執行邏輯以下圖所示:
從源碼能夠看出,CountDownLatch是依賴於AbstractQueuedSynchronizer來實現這一系列邏輯的。
隊列同步器AbstractQueuedSynchronizer
是一個用來構建鎖和同步器的框架,它在內部定義了一個被標識爲volatile的名爲state的變量,用來表示同步狀態。
多個線程之間能夠經過AQS來獨佔式或共享式的搶佔資源。
而且它經過內置的FIFO隊列來完成線程的排隊工做。
CountDownLatch中的Sync會優先嚐試修改state的值,來獲取同步狀態。例如,若是某個線程成功的將state的值從0修改成1,表示成功的獲取了同步狀態。 這個修改的過程是經過CAS完成的,因此能夠保證線程安全。
反之,若是修改state失敗,則會將當前線程加入到AQS的隊列中,並阻塞線程。
CountDownLatch(int N) 中的計數器,可讓咱們支持最多等待N個線程的操做完成,或是一個線程操做N次。
若是僅僅只須要等待線程的執行完畢,那麼join可能就能知足。可是若是須要靈活的控制線程,使用CountDownLatch。
countDownLatch.countDown();
這一句話儘可能寫在finally中,或是保證此行代碼前的邏輯正常運行,由於在一些狀況下,出現異常會致使沒法減一,而後出現死鎖。
CountDownLatch 是一次性使用的,當計數值在構造函數中初始化後,就不能再對其設置任何值,當 CountDownLatch 使用完畢,也不能再次被使用。
爲了方便你們學習討論,我建立了一個java疑難攻堅互助你們庭,和其餘傳統的學習交流不一樣。本羣主要致力於解決項目中的疑難問題,在遇到項目難以解決的
問題時,均可以在這個你們庭裏尋求幫助。
公衆號回覆【問題的答案】進入:java中Integer包裝類的基本數據類型是?
若是你也經歷過遇到項目難題,無從下手,
他人有可能能夠給你提供一些思路和見解,一百我的就有一百種思路,
一樣,若是你也樂於幫助別人,那解決別人遇到的問題,也一樣對你是一種鍛鍊。
歡迎來公衆號【俠夢的開發筆記】 ,回覆乾貨,領取精選學習視頻一份