併發編程經常使用工具類(一) countDownLatch和cyclicBarrier的使用對比

1.CountDownLatch

          countDownLatch的做用是讓一組線程等待其餘線程完成工做之後在執行,至關於增強版的join(不懂能夠百度一下join的用法),通常在初始化的時候會在構造方法傳入計數器,算法

後續,在其餘線程中每次調用countDown方法計數器減一,通常在須要等待的線程中調用countDownLatch的await方法阻塞線程,在當計數器爲0時,等待線程繼續運行。多線程

          光看上面的定義描述不是很直觀,咱們再來結合代碼看一下實際運用:併發

 1 public class UseCountDownLatch {
 2     
 3     static CountDownLatch latch = new CountDownLatch(6);
 4     //初始化線程(只有一步,有4個)
 5     private static class InitThread implements Runnable{
 6 
 7         @Override
 8         public void run() {
 9             System.out.println("Thread_"+Thread.currentThread().getId()
10                     +" ready init work......");
11             latch.countDown();//初始化線程完成工做了,countDown方法只扣減一次;
12             for(int i =0;i<2;i++) {
13                 System.out.println("Thread_"+Thread.currentThread().getId()
14                         +" ........continue do its work");
15             }
16         }
17     }
18     //業務線程
19     private static class BusiThread implements Runnable{
20 
21         @Override
22         public void run() {
23             try {
24                 latch.await();
25             } catch (InterruptedException e) {
26                 e.printStackTrace();
27             }
28             for(int i =0;i<3;i++) {
29                 System.out.println("BusiThread_"+Thread.currentThread().getId()
30                         +" do business-----");
31             }
32         }
33     }
34 
35     public static void main(String[] args) throws InterruptedException {
36         //單獨的初始化線程,初始化分爲2步,須要扣減兩次
37         new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 SleepTools.ms(1);
41                 System.out.println("Thread_"+Thread.currentThread().getId()
42                         +" ready init work step 1st......");
43                 latch.countDown();//每完成一步初始化工做,扣減一次
44                 System.out.println("begin step 2nd.......");
45                 SleepTools.ms(1);
46                 System.out.println("Thread_"+Thread.currentThread().getId()
47                         +" ready init work step 2nd......");
48                 latch.countDown();//每完成一步初始化工做,扣減一次
49             } 
50         }).start();
51         new Thread(new BusiThread()).start();
52         for(int i=0;i<=3;i++){
53             Thread thread = new Thread(new InitThread());
54             thread.start();
55         }
56 
57         latch.await();
58         System.out.println("Main do ites work........");
59     }
60 }

 

運行結果能夠看到,兩個初始化線程先跑,當兩個初始化線程跑完了,latch的計數器減爲0,阻塞放開,主線程和業務線程繼續往下運行。可是,在設計這部分算法的時候須要注意,有可能會出現計數器沒有減爲0,則線程一直阻塞,致使程序卡死。app

          countDownLatch通常使用在多線程併發以後須要對結果進行處理,而咱們沒法控制全部線程的執行時間,因此在這裏加上阻塞,等到只有線程所有執行完。dom

 

2.CyclicBarrier

          cyclicBarrier從業務角度來講,和countDownLatch比較相似(具體對比後面會專門介紹),其做用相似一個屏障(英文中也是屏障的意思),阻隔線程直到所有到達屏障點,放開屏障。通常能夠用在多線程統計的時候,ide

          從代碼角度來看,cyclicBarrier有兩個構造方法,CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction);ui

          兩個方法的區別是第一個只是記錄了線程計數器的個數,而第二個不只記錄了計數器,當屏障放開時,會執行第二個參數線程的方法(第二個參數通常傳入的是一個實現了Runnable的線程方法)spa

再來結合代碼深刻了解一下:線程

          

 1 public class UseCyclicBarrier {
 2     
 3     private static CyclicBarrier barrier 
 4         = new CyclicBarrier(5,new CollectThread());
 5     
 6     private static ConcurrentHashMap<String,Long> resultMap
 7             = new ConcurrentHashMap<>();//存放子線程工做結果的容器
 8 
 9     public static void main(String[] args) {
10         new Thread(){
11             @Override
12             public void run() {
13                 long id = Thread.currentThread().getId();//線程自己的處理結果
14                 resultMap.put(Thread.currentThread().getId()+"",id);
15                 Random r = new Random();//隨機決定工做線程的是否睡眠
16                 try {
17                     if(r.nextBoolean()) {
18                         Thread.sleep(2000+id);
19                         System.out.println("Thread_"+id+" ....do something ");
20                     }
21                     System.out.println(id+"....is await");
22                     barrier.await();
23                     Thread.sleep(1000+id);
24                     System.out.println("Thread_"+id+" ....do its business ");
25                 } catch (Exception e) {
26                     e.printStackTrace();
27                 }
28             }
29         }.start();
30         for(int i=0;i<=3;i++){
31             Thread thread = new Thread(new SubThread());
32             thread.start();
33         }
34     }
35 
36     //負責屏障開放之後的工做
37     private static class CollectThread implements Runnable{
38 
39         @Override
40         public void run() {
41             StringBuilder result = new StringBuilder();
42             for(Map.Entry<String,Long> workResult:resultMap.entrySet()){
43                 result.append("["+workResult.getValue()+"]");
44             }
45             System.out.println(" the result = "+ result);
46             System.out.println("do other business........");
47         }
48     }
49 
50     //工做線程
51     private static class SubThread implements Runnable{
52 
53         @Override
54         public void run() {
55             long id = Thread.currentThread().getId();//線程自己的處理結果
56             resultMap.put(Thread.currentThread().getId()+"",id);
57             Random r = new Random();//隨機決定工做線程的是否睡眠
58             try {
59                 if(r.nextBoolean()) {
60                     Thread.sleep(2000+id);
61                     System.out.println("Thread_"+id+" ....do something ");
62                 }
63                 System.out.println(id+"....is await");
64                 barrier.await();
65                 Thread.sleep(1000+id);
66                 System.out.println("Thread_"+id+" ....do its business ");
67             } catch (Exception e) {
68                 e.printStackTrace();
69             }
70         }
71     }
72 }

   輸出結果:       11....is await
                            12....is await
                             Thread_10 ....do something
                     10....is await
Thread_13 ....do something
13....is await
Thread_14 ....do something
14....is await
 the result = [11][12][13][14][10]
do other business........
Thread_10 ....do its business
Thread_11 ....do its business
Thread_12 ....do its business
Thread_13 ....do its business
Thread_14 ....do its business設計

 

3.countDownLatch和cyclicBarrier區別

        (1)countDownlatch的計數器由調用countDown方法次數決定,每次調用計數器減一,能夠在一個線程中調用屢次,而cyclicBarrier計數器取決調用await方法的線程個數。

          (2)  countDownLatch只能用一次,而cyclicBarrier能夠循環使用。而且cyclicBarrier能夠調用reset方法重置計數器,能夠在線程故障從新啓用線程調用。

          (3)  兩者在內部方法也有不少區別,具體有興趣的能夠去看看源碼。

 

 參考文章:http://ifeve.com/concurrency-cyclicbarrier/

相關文章
相關標籤/搜索