java併發編程JUC第九篇:CountDownLatch線程同步

在之前的文章中已經爲大家介紹了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.

歡迎關注我的博客,裏面有很多精品合集

本文轉載註明出處(必須帶連接,不能只轉文字):字母哥博客 - zimug.com

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章