【Java】【多線程】CountdownLatch使用方法說明

CountDownLatch基本概念

CountDownLatch是在java1.5被引入的,CountDownLatch是一個同步工具類,用來協調多個線程之間的同步(或者說是線程之間的通信)。 CountDownLatch能夠使一個線程在等待另外一些線程完成其工作之後,再本線程繼續執行。他使用一個計數器進行實現。當計數器爲0時,表示所有的線程都已經完成一些任務,然後在CountDownLatch上等待的線程就可以恢復執行接下來的任務。

CountDownLatch重點方法介紹

  • public void countDown():構造函數中的數字減1(遞減鎖存器的計數),如果計數到達零,則釋放所有等待(.await())的線程。如果當前計數大於零,則將計數減少.
  • public boolean await():
使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷。 
如果當前計數爲零,則此方法立即返回。 
如果當前計數大於零,則出於線程調度目的,將禁用當前線程,且在發生以下兩種情況之一前,該線程將一直處於休眠狀態:
1)由於調用 countDown() 方法,計數到達零;或者其他某個線程中斷當前線程。 
2)如果當前線程在進入此方法時已經設置了該線程的中斷狀態;或者在等待時被中斷,則拋出 InterruptedException,並且清除當前線程的已中斷狀態。 
  • public boolean await(long timeout,TimeUnit unit):
參數:
  timeout - 要等待的最長時間
  unit - timeout 參數的時間單位。 
返回:
  如果計數到達零,則返回 true;如果在計數到達零之前超過了等待時間,則返回 false 
異常: 
   InterruptedException - 如果當前線程在等待時被中斷

CountDownLatch適用場景說明

CountdownLatch核心兩個作用:

  1. 多個線程等待同一個信號量一起工作。模擬真實併發的場景。
  2. 多個線程全部做完了,纔開始做主線程工作。

下面的使用案例部分講使用代碼講解CountDownLatch的使用案例

CountDownLatch使用案例

案例場景:百米賽跑,4名運動員選手到達場地等待裁判口令,裁判一聲口令,選手聽到後同時起跑,當所有選手到達終點,裁判進行彙總排名.(注意線程池的初始大小數量4)

public class CountdownLatchDemo {
    public static void main(String[] args) {

        /** 線程池使用規範見:本子包中的TreadPoolDemo類 */
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(" %d號運動員 ").build();
        ExecutorService threadPool =
                new ThreadPoolExecutor(4,8,0L,TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(1024),namedThreadFactory,
                    new ThreadPoolExecutor.AbortPolicy());

        final CountDownLatch cdOrder = new CountDownLatch(1);
        final CountDownLatch cdPlayers = new CountDownLatch(4);
        for (int i = 0; i < 4; i++) {
            Runnable runnable = () -> {
                try {
                    System.out.println("選手" + Thread.currentThread().getName() + "正在等待裁判發佈口令");
                    cdOrder.await();
                    System.out.println("選手" + Thread.currentThread().getName() + "已接受裁判口令");
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("選手" + Thread.currentThread().getName() + "到達終點");
                    cdPlayers.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("線程出現了異常"+e.getMessage());
                }
            };
            threadPool.execute(runnable);
        }
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("裁判"+Thread.currentThread().getName()+"即將發佈口令");
            cdOrder.countDown();
            System.out.println("裁判"+Thread.currentThread().getName()+"已發送口令,正在等待所有選手到達終點");
            cdPlayers.await();
            System.out.println("所有選手都到達終點");
            System.out.println("裁判"+Thread.currentThread().getName()+"彙總成績排名");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("線程出現了異常"+e.getMessage());
        }
        threadPool.shutdown();
    }
}

運行結果查看

選手 2號運動員 正在等待裁判發佈口令
選手 0號運動員 正在等待裁判發佈口令
選手 3號運動員 正在等待裁判發佈口令
選手 1號運動員 正在等待裁判發佈口令
裁判main即將發佈口令
裁判main已發送口令,正在等待所有選手到達終點
選手 2號運動員 已接受裁判口令
選手 1號運動員 已接受裁判口令
選手 3號運動員 已接受裁判口令
選手 0號運動員 已接受裁判口令
選手 3號運動員 到達終點
選手 1號運動員 到達終點
選手 0號運動員 到達終點
選手 2號運動員 到達終點
所有選手都到達終點
裁判main彙總成績排名

最後各位讀者思考或者驗證下面2種情況
1:如果將代碼中第7行的new ThreadPoolExecutor(4, 修改成 new ThreadPoolExecutor(3, 會有什麼結果輸出?爲什麼?
2:本示例代碼沒有將裁判開始槍響 作爲 運動員開始的信號量,如果要這樣修改,需要怎麼做呢? 動手試試吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章