java併發程式設計JUC第九篇:CountDownLatch執行緒同步

字母哥部落格發表於2021-06-18

在之前的文章中已經為大家介紹了java併發程式設計的工具:BlockingQueue介面、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque介面、ConcurrentHashMap,本文為系列文章第九篇。

CountDownLatch是一種執行緒同步輔助工具,它允許一個或多個執行緒等待其他執行緒正在執行的一組操作完成。CountDownLatch的概念在java併發程式設計中非常常見,面試也會經常被問到,所以一定要好好理解掌握。在這篇文章中,我將介紹以下幾點

  • CountDownLatch是什麼?
  • CountDownLatch 如何工作
  • CountDownLatch 程式碼例子

CountDownLatch是什麼?

CountDownLatch與其他併發程式設計工具類,如CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue等在java.util.concurrent包中與JDK 1.5一起被引入。CountDownLatch能讓一個java執行緒等待其他執行緒完成任務,比如Application的主執行緒等待,直到其他負責啟動框架服務的服務執行緒完成所有服務的啟動。

CountDownLatch用執行緒數來初始化一個計數器,每當一個執行緒完成執行時,這個計數器就會遞減。當計數為零時,表示所有執行緒都已完成執行,處於等待狀態的主執行緒可以繼續執行。

下面我們使用虛擬碼的方式描述CountDownLatch 的作用

  • 主執行緒啟動,併為N個執行緒(假設n=3)初始化CountDownLatch(n)
  • 啟動n個執行緒
  • 主執行緒阻塞等待
  • 執行緒1執行完成,CountDownLatch -1 = 2,主執行緒繼續阻塞
  • 執行緒3執行完成,CountDownLatch -1 = 1,主執行緒繼續阻塞
  • 執行緒4執行完成,CountDownLatch -1 = 0,主執行緒恢復執行

CountDownLatch 如何工作

CountDownLatch.java類裡面定義了一個建構函式。count實質上是執行緒數,這個值只能設定一次,CountDownLatch沒有提供方法來重置這個數

CountDownLatch.public CountDownLatch(int count) {...}

使用CountDownLatch的主執行緒要去等待其他執行緒執行完成,所以這個主執行緒必須在啟動其他執行緒後立即呼叫 CountDownLatch.await() 方法,該方法阻塞主執行緒處於等待狀態,直到其他執行緒執行完畢,才會停止阻塞。

其他N個執行緒必須有CountDownLatch物件的引用,因為它們需要通知CountDownLatch物件它們已經完成任務。這個通知是由方法CountDownLatch.countDown()來完成的,每呼叫一次該方法,就會將建構函式中設定的初始計數count減少1,所以當所有N個執行緒都呼叫了這個方法後count計數達到0,主執行緒就可以不受await()方法阻塞恢復執行了。

所以CountDownLatch特別適合於那些需要等待N個執行緒完成後再開始執行的場景。例如一個應用程式的啟動類,在處理使用者請求之前,要確保所有N個外部系統都是處於執行狀態的。

CountDownLatch 程式碼例子

假設我們的應用程式主執行緒啟動之前,要檢查另外4個程式是否準備就緒,只有其他的4個程式準備就緒,我們的主程式才能繼續執行。就可以使用下面的程式碼來操作:

import java.util.concurrent.CountDownLatch;

public class Tester {
   public static void main(String args[]) {
      //設定計數器 counter = 4 ,等於執行緒數
      CountDownLatch countDownLatch = new CountDownLatch(4);

      Thread app1 = new Thread(new Application("App1",  countDownLatch));
      Thread app2 = new Thread(new Application("App2",  countDownLatch));          
      Thread app3 = new Thread(new Application("App3",  countDownLatch));
      Thread app4 = new Thread(new Application("App4",  countDownLatch));  
 
      // 啟動多執行緒去檢查其他四個程式的可用狀態
      app1.start();
      app2.start();
      app3.start();
      app4.start();

      try {
         //主執行緒呼叫await進行等待,等待上述四個執行緒正常完成
         countDownLatch.await();            
         //上述四個執行緒檢查的應用程式啟動正常之後, 列印如下資訊
         System.out.println("All applications are up and running.");
      } catch(InterruptedException e) {
         System.out.println(e.getMessage());
      }        
   }
}

子執行緒程式,每一個執行緒都持有countDownLatch物件,執行緒正常執行完成之時,使用countDownLatch.countDown()方法將countDownLatch物件的計數器減1。

class Application implements Runnable {
   private String name; //應用程式名稱
   private CountDownLatch countDownLatch; 

   public Application(String name, CountDownLatch countDownLatch) {
      this.name = name;
      this.countDownLatch = countDownLatch;
   }

   public void run() {
      try {
         System.out.println(name + " started. ");
         Thread.sleep(1000);
      } catch (InterruptedException e) {
         System.out.println(e.getMessage());
      }
      System.out.println( name + " is Up and running.");
      //將countDownLatch計數器的值減1
      countDownLatch.countDown();    
   }
}

上述程式的列印輸出結果是,可以結合輸出結果去理解上文中講述的CountDownLatch 工作原理:

App2 started.  
App3 started.  
App1 started.  
App4 started.  
App1 is Up and running.
App3 is Up and running.
App4 is Up and running.
App2 is Up and running.
All applications are up and running.

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章