CountDownLatch計數器閉鎖是一個能阻塞主線程,讓其他線程滿足特定條件下主線程再繼續執行的線程同步工具。
Latch閉鎖的意思,是一種同步的工具類。類似於一扇門:在閉鎖到達結束狀態之前,這扇門一直是關閉着的,不允許任何線程通過,當到達結束狀態時,這扇門會打開並允許所有的線程通過。且當門打開了,就永遠保持打開狀態。
CountDowmLatch是一種靈活的閉鎖實現,包含一個計數器,該計算器初始化為一個正數,表示需要等待事件的數量。countDown方法遞減計數器,表示有一個事件發生,而await方法等待計數器到達0,表示所有需要等待的事情都已經完成。
這句話的意思是:在單獨主線程的情況下,創建多個子線程, 在保證主線程同步的情況下,同時又讓多個子線程處理,可以使用 CountDownLatch來阻塞主線程,等待子線程執行完成,主線程才執行后續操作.
常見案例:
1:多線程讀取批量文件, 並且讀取完成之后匯總處理
2:多線程讀取Excel多個sheet,讀取完成之后獲取匯總獲取的結果
3:多個人一起一起來吃飯,主人等待客人到來,客人一個個從不同地方來到飯店,主人需要等到所有人都到來之后,才能開飯
4:汽車站,所有乘客都從不同的地方趕到汽車站,必須等到所有乘客都到了,汽車才會出發,如果設置了超時等待,那么當某個時間點到了,汽車也出發
作用:可以用來確保某些活動直到其他活動都完成后才繼續執行。
注意事項:
使用CountDownLatch必須確保計數器數量與子線程數量一致,且countDown必須要執行,否則出現計數器不為0,導致主線程一致等待的情況
在執行任務的線程中,使用了try...finally結構,該結構可以保證創建的線程發生異常時CountDownLatch.countDown()方法也會執行,也就保證了主線程不會一直處於等待狀態。
CountDownLatch非常適合於對任務進行拆分,使其並行執行,比如某個任務執行2s,其對數據的請求可以分為五個部分,那么就可以將這個任務拆分為5個子任務,分別交由五個線程執行,執行完成之后再由主線程進行匯總,此時,總的執行時間將決定於執行最慢的任務,平均來看,還是大大減少了總的執行時間。
具體代碼示例:
模擬乘客登機的場景
package com.puppy.demo; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; //劉小品 public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { System.out.println("南京祿口機場_機長正在等待所有乘客登機"); //CountDownLatch的計數器數量必須與線程的數量一致,否則可能出現一直等待的情況,即計數器不為0的情況 //使用CountDownLatch 需要注意,子線程中的countDown()最好放到finnally里面,防止計數器不為0 CountDownLatch latch = new CountDownLatch(3); ExecutorService ex = Executors.newCachedThreadPool(); ex.execute(new ThreadTest1(latch)); ex.execute(new ThreadTest2(latch)); ex.execute(new ThreadTest3(latch)); //等待所有乘客來機場 System.out.println("所有乘客都在趕飛機的路上"); //latch.await(1,TimeUnit.SECONDS);//模擬超時等待的情況 latch.await(); //模擬等待的情況,不考慮子線程的處理實際 System.out.println("南京祿口機場_機長啟動飛機起飛"); ex.shutdown(); } } class ThreadTest1 extends Thread { CountDownLatch lanch; public ThreadTest1() { } public ThreadTest1(CountDownLatch lanch) { this.lanch = lanch; } @Override public void run() { try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("正在馬鞍山,准備趕到南京坐飛機,需要1小時的車程到機場"); lanch.countDown(); } } class ThreadTest2 extends Thread { CountDownLatch lanch; public ThreadTest2() { } public ThreadTest2(CountDownLatch lanch) { this.lanch = lanch; } @Override public void run() { System.out.println("正在徐州,准備趕到南京坐飛機,需要5小時的車程到機場"); lanch.countDown(); } } class ThreadTest3 extends Thread { CountDownLatch lanch; public ThreadTest3() { } public ThreadTest3(CountDownLatch lanch) { this.lanch = lanch; } @Override public void run() { System.out.println("正在蕪湖,准備趕到南京坐飛機,需要2小時的車程到機場"); lanch.countDown(); } }
執行結果
latch.await()
南京祿口機場_機長正在等待所有乘客登機 所有乘客都在趕飛機的路上 正在徐州,准備趕到南京坐飛機,需要5小時的車程到機場 正在蕪湖,准備趕到南京坐飛機,需要2小時的車程到機場 正在馬鞍山,准備趕到南京坐飛機,需要1小時的車程到機場 南京祿口機場_機長啟動飛機起飛
超時等待的情況:latch.await(1,TimeUnit.SECONDS)
主線程已經執行完成,但是子線程才結束,latch.await(1,TimeUnit.SECONDS)方法
南京祿口機場_機長正在等待所有乘客登機 所有乘客都在趕飛機的路上 正在徐州,准備趕到南京坐飛機,需要5小時的車程到機場 正在蕪湖,准備趕到南京坐飛機,需要2小時的車程到機場 南京祿口機場_機長啟動飛機起飛 正在馬鞍山,准備趕到南京坐飛機,需要1小時的車程到機場