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
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) 兩者在內部方法也有不少區別,具體有興趣的能夠去看看源碼。