並發編程 04——閉鎖CountDownLatch 與 柵欄CyclicBarrier


概述

第1部分 閉鎖

第2部分 柵欄

參考

 

第1部分 閉鎖

  閉鎖是一種同步工具類,可以延遲線程的進度直到其到達終止狀態。閉鎖的作用相當於一扇門:在閉鎖到達結束狀態之前,這扇門一直是關閉的,並且沒有任何線程能通過,當到達結束狀態時,這扇門會打開並允許所有的線程通過。當閉鎖到達結束狀態后,將不會再改變狀態,因此這扇門將永遠保持打開狀態。閉鎖可以用來確保某些活動直到其他活動都完成后才繼續執行。例如:

  • 確保某個計算在其需要的所有資源都被初始化之后才繼續執行。
  • 確保某個服務在其依賴的所有其他服務都已經啟動之后才啟動。
  • 等待直到某個操作的所有參與者都就緒再繼續執行。

   CountDownLatch是一種靈活的閉鎖實現,可以再上述各種情況中使用,它可以使一個或多個線程等待一組事件發生。閉鎖狀態包括一個計數器,該計數器被初始化為一個正數,表示需要等待的事件數量。countDown方法遞減計數器,表示有一個事件已經發生了,而await方法等待計數器達到0,這表示所有需要等待的事件都已經發生。如果計數器的值非零,那么await會一直阻塞直到計數器為零,或者等待中的線程中斷,或者等待超時。

  下面程序TestHarness中給出了閉鎖的兩種常見用法。TestHarness創建一定數量的線程,利用它們並發地執行指定的任務。它使用兩個閉鎖,分別表示“起始門”和“結束門”。起始門計數器的初始值為1,而結束門計數器的初始值為工作線程的數量。每個工作線程首先要做到就是在啟動門上等待,從而確保所有線程都就緒后才開始執行。而每個線程要做的最后一個事情是將調用結束門的countDown方法減1 ,這能使主線程高效低等待直到所有工作線程都執行完畢,因此可以統計所消耗的時間。

 1 package com.concurrency.BasicBuildingBlocks_5;
 2 
 3 import java.util.concurrent.CountDownLatch;
 4 
 5 /**
 6  * 5.11 在計時測試中使用CountDownLatch來啟動和停止線程(閉鎖)
 7  * 
 8  * @ClassName: TestHarness
 9  * TODO
10  * @author Xingle
11  * @date 2014-9-4 下午2:56:29
12  */
13 public class TestHarness {
14     
15     int nThreads ;
16     Runnable task;
17     public TestHarness(int nThreads,Runnable task){
18         this.nThreads = nThreads;
19         this.task = task;
20     } 
21      public long timeTask(){
22          
23         //起始門
24         final CountDownLatch startGate = new CountDownLatch(1);
25         //結束門
26         final CountDownLatch endGate = new CountDownLatch(nThreads);
27          for(int i = 0;i<nThreads;i++){
28              Thread thread = new Thread(){
29                  public void run(){
30                      //每個線程在啟動門上等待,確保所有線程都就緒后才開始
31                      try {
32                         startGate.await();//等待計數器達到0 
33                         try{
34                             task.run();
35                         }finally{
36                             //每個線程結束后,調用countDown遞減計數器,表示一個事件發生
37                             endGate.countDown();
38                         }
39                     } catch (InterruptedException e) {
40                         e.printStackTrace();
41                     }
42                      
43                  }
44              };
45              thread.start();
46          }
47          long start = System.nanoTime();
48          //啟動門發生
49          startGate.countDown();
50          try {
51              //等待結束門的線程都結束
52             endGate.await();
53         } catch (InterruptedException e) {
54             e.printStackTrace();
55         }
56         long end = System.nanoTime();
57         return end - start;
58      }
59 }

 

測試程序:

 1 package com.concurrency.BasicBuildingBlocks_5;
 2 
 3 /**
 4  * 
 5  * @ClassName: TestHarnessMain
 6  * TODO
 7  * @author Xingle
 8  * @date 2014-9-4 下午3:27:18
 9  */
10 public class TestHarnessMain {
11 
12     public static void main(String[] args){
13         Runnable task = new Runnable() {
14             
15             @Override
16             public void run() {
17                 System.out.println("執行任務,我是線程:"+Thread.currentThread().getName());                
18             }
19         };
20         int count = 10;
21         TestHarness testHarness = new TestHarness(count, task );
22         long time = testHarness.timeTask();
23         System.out.println("閉鎖  測試結果  執行"+count+"個線程"+" 一共用時:"+time);
24     }
25 }

 

執行結果:

第2部分 柵欄

上面已經看到通過閉鎖來啟動一組相關的操作,或者等待一組相關的操作結束。閉鎖是一次性對象,一旦進入終止狀態,就不能被重置。

  柵欄(Barrier)類似於閉鎖,它能阻塞一組線程直到某個事件發生。柵欄與閉鎖的關鍵區別在於,所有線程必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其他線程。(柵欄則是所有線程相互等待,直到所有線程都到達某一點時才打開柵欄,然后線程可以繼續執行。)

  CyclicBarrier 可以使一定數量的參與方反復地在柵欄位置匯集,它在並行迭代算法中非常有用。CyclicBarrier支持一個可選的 Runnable 參數,當線程通過柵欄時,runnable對象將被調用。構造函數CyclicBarrier(int parties, Runnable barrierAction),當線程在CyclicBarrier對象上調用await()方法時,柵欄的計數器將增加1,當計數器為parties時,柵欄將打開。

 1 package com.concurrency.BasicBuildingBlocks_5;
 2 
 3 import java.util.concurrent.BrokenBarrierException;
 4 import java.util.concurrent.CyclicBarrier;
 5 
 6 /**
 7  * 
 8  * @ClassName: Worker 
 9  * @author Xingle
10  * @date 2014-9-9 上午10:41:17
11  */
12 public class Worker implements Runnable {
13 
14     final int id;
15     final CyclicBarrier barrier;
16 
17     public Worker(int id, CyclicBarrier barrier) {
18         this.id = id;
19         this.barrier = barrier;
20     }
21 
22     @Override
23     public void run() {
24         System.out.println(this.id + " start to run!");
25         try {
26             this.barrier.await();
27         } catch (InterruptedException e) {
28             e.printStackTrace();
29         } catch (BrokenBarrierException e) {
30             e.printStackTrace();
31         }
32 
33     }
34 
35 }

 

測試程序:

 1 package com.concurrency.BasicBuildingBlocks_5;
 2 
 3 import java.util.concurrent.CyclicBarrier;
 4 
 5 /**
 6  * 
 7  * @ClassName: Beer
 8  * 有五個人參與跑步,規定五個人只要都跑到終點了,大家可以喝啤酒。但是,只要有一個人沒到終點,就不能喝。 這里沒有要求大家要同時起跑
 9  * @author Xingle
10  * @date 2014-9-9 上午10:40:36
11  */
12 public class Beer {
13     public static void main(String[] args){
14         final int count = 5;
15         final CyclicBarrier barrier = new CyclicBarrier(count, new Runnable() {
16             
17             @Override
18             public void run() {
19                 System.out.println("drink beer!");
20             }
21         });
22         
23         for (int i =0; i<count;i++){
24             new Thread(new Worker(i, barrier)).start();
25         }
26     }
27     
28 }

 

執行結果:

 


 

參考:

1.《並發編程實戰》 5.5 同步工具類

2.盡量把CyclicBarrier和CountDownLatch的區別說通俗點

 


免責聲明!

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



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