Java多線程並發系列之閉鎖(Latch)和柵欄(CyclicBarrier)



  

JAVA並發包中有三個類用於同步一批線程的行為,分別是閉鎖(Latch),信號燈(Semaphore)和柵欄(CyclicBarrier)。本貼主要說明閉鎖(Latch)和柵欄(CyclicBarrier)。

1. 閉鎖(Latch

閉鎖Latch)  —— 確保多個線程在完成各自事務后,才會打開繼續執行后面的內容,否則一直等待。

計數器閉鎖CountDownLatch) —— 是JDK5+ 里面閉鎖的一個實現,允許一個或多個線程等待某個事件的發生。CountDownLatch  有個正數的計數器,countDown(); 對計數器做減法操作,await(); 等待計數器 = 0。所有await的線程都會阻塞,直到計數器為0或者等待線程中斷或者超時。

 1 public static void main(String[] args) throws InterruptedException {
 2         // 申明,等待事件數量 5次
 3         CountDownLatch await = new CountDownLatch(5);
 4  
 5         // 依次創建並啟動處於等待狀態的5個MyRunnable線程
 6         for (int i = 1; i < 6; ++i) {
 7             new Thread(new MyRunnable(await, i)).start();
 8         }
 9  
10         System.out.println("等待線程開始工作......");
11         await.await();
12         System.out.println("結束!");
13 }
 1 public static class MyRunnable implements Runnable {
 2  
 3     private final CountDownLatch await;
 4     private final int num;
 5  
 6     public MyRunnable(CountDownLatch await, int num) {
 7         this.await = await;
 8         this.num  =  num;
 9     }
10  
11     public void run() {
12         try {
13             System.out.println("線程"+num+"執行完畢。");
14             await.countDown(); // 當前事件執行完畢,計數 -1
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18     }
19 } 
運行結果:
等待線程開始工作......
線程1執行完畢。
線程2執行完畢。
線程3執行完畢。
線程4執行完畢。
線程5執行完畢。
結束!

 

流程如圖所示:

 圖1 - CountDownLatch 處理流程

 

2. 柵欄(CyclicBarrier

柵欄類似於閉鎖,它能阻塞一組線程直到某個事件發生。 柵欄與閉鎖的關鍵區別在於,所有的線程必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其他線程。

場景: 比如甲乙丙三人一把椅子,甲做椅子腿,乙做椅子面,丙做椅子靠背。等3人都做成后,就可以組裝成椅子了。這是一種並行迭代,將一個問題分成很多子問題,當一系列的子問題都解決之后(所有子問題線程都已經await(); ),此時將柵欄打開,所有子問題線程被釋放,而柵欄位置可以留着下次使用。

 示例如下:

1 public static void main(String[] args) throws InterruptedException {
2     // 申明,等待線程數量 3次
3     CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
4 
5     // 依次創建並啟動處於等待狀態的3個MyRunnable2線程
6     new Thread(new ChairRunnable(cyclicBarrier, "椅子腿")).start();
7     new Thread(new ChairRunnable(cyclicBarrier, "椅子面")).start();
8     new Thread(new ChairRunnable(cyclicBarrier, "椅子背")).start();
9 }
 1 public static class ChairRunnable implements Runnable {
 2     private final CyclicBarrier cyclicBarrier;
 3     private final String event;
 4 
 5     public ChairRunnable(CyclicBarrier cyclicBarrier, String event) {
 6         this.cyclicBarrier = cyclicBarrier;
 7         this.event = event;
 8     }
 9 
10     public void run() {
11         try {
12             System.out.println("開始做【" + event + "】。");
13             Thread.sleep(new Random().nextInt(10000));
14             cyclicBarrier.await(); // 等待其他線程完成
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         } catch (BrokenBarrierException e) {
18             e.printStackTrace();
19         }
20         System.out.println("【" + event + "】做好了, 我們來一起組裝吧!");
21     }
22 }
運行結果:
開始做【椅子腿】。
開始做【椅子背】。
開始做【椅子面】。
【椅子面】做好了, 我們來一起組裝吧!
【椅子腿】做好了, 我們來一起組裝吧!
【椅子背】做好了, 我們來一起組裝吧!

 

流程如下圖所示:

圖2 - CyclicBarrier 處理流程

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM