概念
CountDownLatch 是一個同步工具類,它允許一個或多個線程一直等待,直到其他線程執行完后再執行。例如,應用程序的主線程希望在負責啟動框架服務的線程已經啟動所有的框架服務之后執行。
CountDownlatch 原理
通過一個計數器來實現的,計數器的初始值為線程的數量。每當一個線程完成了自己的任務后,計數器的值就相應的減1 。當計數器到達 0 時,表示所有的線程都已完成了任務,然后在閉鎖上等待的線程就可以恢復執行任務。
CountDownLatch的用法
CountDownlacth典型用法1:
某一個線程開始運行前等待n個線程執行完畢。將 CountDownLatch 的計數器初始化為n (new CountDownLatch(n)),每當一個任務線程執行完畢,就將計數器減1 (countdownlatch.countDown() ),當計數器的值變為0時,在CountDownLatch上 await()
的線程就會被喚醒。一個典型應用場景就是啟動一個服務時,主線程需要等待多個組件加載完畢,之后再繼續執行。
需要注意的是:
await有多種方法,無限制時間等待 和 有限制時間等待
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); }
使用例子:
public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { new Thread(new Runnable(){ @Override public void run(){ System.out.println(Thread.currentThread().getName()+" 正在運行"); try { Thread.sleep(3000); }catch( Exception e){ e.printStackTrace(); } finally { latch.countDown(); } } }).start(); } System.out.println("等待子線程運行結束"); latch.await(); //latch.await(10, TimeUnit.SECONDS); System.out.println("子線程運行結束"); }
CountDownLatch典型用法2:
實現多個線程開始執行任務的最大並行性。注意是並行性,不是並發,強調的是多個線程在某一時刻同時開始執行。類似於賽跑,將多個線程放到起點,等待發令槍響,然后同時開跑。做法是初始化一個共享的CountDownLatch(1),將其計數器初始化為1,多個線程在開始執行任務前首先 coundownlatch.await(),當主線程調用 countDown() 時,計數器變為0,多個線程同時被喚醒。
子線程等待主線程處理完畢開始處理,子線程處理完畢后,主線程輸出
class MyRunnable implements Runnable { private CountDownLatch countDownLatch; private CountDownLatch await; public MyRunnable(CountDownLatch countDownLatch, CountDownLatch await) { this.countDownLatch = countDownLatch; this.await = await; } @Override public void run() { try { countDownLatch.await();// 所有線程在此進入等待狀態 System.out.println("子線程" +Thread.currentThread().getName()+ "處理自己事情"); Thread.sleep(1000); await.countDown();// 所有線程在此進入等待狀態
} catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch await = new CountDownLatch(5); for (int i=0; i< 5; i++) { new Thread(new MyRunnable(countDownLatch, await)).start(); } System.out.println("主線程處理自己事情"); Thread.sleep(3000); countDownLatch.countDown();// 主線程調用countDown() 時,計數器變為0,所有線程去做要做的事 System.out.println("主線程處理結束"); await.await();// 喚醒所有線程 System.out.println("子線程處理完畢啦"); }
在實時系統中的使用場景
實現最大的並行性:有時我們想同時啟動多個線程,實現最大程度的並行性。例如,我們想測試一個單例類。如果我們創建一個初始計數器為1的CountDownLatch,並讓其他所有線程都在這個鎖上等待,只需要調用一次countDown()方法就可以讓其他所有等待的線程同時恢復執行。
開始執行前等待N個線程完成各自任務:例如應用程序啟動類要確保在處理用戶請求前,所有N個外部系統都已經啟動和運行了。
死鎖檢測:一個非常方便的使用場景是你用N個線程去訪問共享資源,在每個測試階段線程數量不同,並嘗試產生死鎖。
————————————————
版權聲明:本文為CSDN博主「春風十里不及你」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq812908087/article/details/81112188