1. 程式人生 > >Java 併發程式設計系列之閉鎖(CountDownLatch)

Java 併發程式設計系列之閉鎖(CountDownLatch)

在講閉鎖之前,我們先來思考一個問題:在多執行緒環境下,主執行緒列印一句話,如何保證這句話最後(其他執行緒全部執行完畢)列印?

博主目前可以想到的實現方式有兩種。一種是通過 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 併發程式設計實戰》