Java 併發程式設計系列之閉鎖(CountDownLatch)
阿新 • • 發佈:2018-12-09
在講閉鎖之前,我們先來思考一個問題:在多執行緒環境下,主執行緒列印一句話,如何保證這句話最後(其他執行緒全部執行完畢)列印?
博主目前可以想到的實現方式有兩種。一種是通過 join()
方法實現,另一種就是用閉鎖。如果大家有好的解決辦法,可以在下面留言,下面進入正題。
一、什麼是閉鎖
閉鎖(CountDownLatch
)是 java.util.concurrent
包下的一種同步工具類。閉鎖可以用來確保某些活動直到其他活動都完成後才執行。
閉鎖相當於一扇門:在閉鎖到達結束狀態之前,這扇門一直是關閉的,並且沒有任何執行緒能通過,當達到結束狀態時,這扇門會開啟,並允許所有的執行緒通過。
下面是 CountDownLatch
CountDownLatch
中有一個計數器欄位,在物件建立時初始化,表示需要等待的事件數量。countDown()
方法遞減計數器,表示有一個事件已經發生了,await()
方法等待計數器為 0 時,表示所有的需要等待的時間都已經發生。如果計數器的值非 0 會一直阻塞直到計數器為 0。
下面使用閉鎖來實現文章開頭的問題:
public class CountDownLatchTest {
/**
* 初始化需要等待的 3 個事件
*/
private static CountDownLatch latch = new CountDownLatch(3 );
public static void main(String[] args) throws InterruptedException {
/**
* 建立 3 個執行緒去執行事件
*/
new Thread(() -> {
System.out.println("*****_*****");
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("*****_*****" );
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("*****_*****");
latch.countDown();
}).start();
// 在計數器為 0 之前會一直阻塞
latch.await();
System.out.println("~~~~~_~~~~~");
}
}
二、閉鎖的用途
我們已經知道了閉鎖的使用方法,但是閉鎖都是有哪些應用場景呢?
- 確保某個計算在其需要的所有資源都被初始化之後才執行
- 確保某個服務在其依賴的所有其他服務都已經啟動之後才啟動
- 等待直到每個操作的所有參與者都就緒再執行(比如打麻將時需要等待四個玩家就緒)
CountDownLatch
底層是基於 AQS(AbstractQueuedSynchronizer
)實現的,關於 AQS 的知識會在後續的博文中進行分析,大家有興趣的可以持續關注。
PS:在一開始提到了使用 join()
方法解決列印問題,下面把程式碼貼出來供大家參考。
public class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> System.out.println("*****_*****"));
Thread thread2 = new Thread(() -> System.out.println("*****_*****"));
Thread thread3 = new Thread(() -> System.out.println("*****_*****"));
thread1.start();
thread2.start();
thread3.start();
/**
* 作用:在 A 執行緒中呼叫了 B 執行緒的 join() 方法時,
* 表示只有當 B 執行緒執行完畢時,A 執行緒才能繼續執行
* 原理:呼叫了當前執行緒的 wait() 方法
*/
thread1.join();
thread2.join();
thread3.join();
System.out.println("~~~~~_~~~~~");
}
}
參考資料
《Java 併發程式設計實戰》