java多線程之倒計時器CountDownLatch

示例一、

原文:https://www.relaxheart.cn/to/master/blog?uuid=83
CountDownLatch是一個非常實用的多線程控制工具類。常用的就下面幾個方法:

CountDownLatch(int count) //實例化一個倒計數器,count指定計數個數
countDown() // 計數減一
await() //等待,當計數減到0時,所有線程並行執行

CountDownLatch在我工作的多個場景被使用,算是用的很頻繁的了,比如我司API接口要求響應時間在200ms以內,但是如果一個接口內部依賴多個三方/外部服務,那串行調用接口的RT必然很久,所以個人用的最多的是接口RT優化場景,內部服務並行調用。
對於倒計數器,一種典型的場景就是火箭發射。在火箭發射前,爲了保證萬無一失,往往還要進行各項設備、儀器的檢測。只有等到所有的檢查完畢後,引擎才能點火。那麼在檢測環節當然是多個檢測項可以同時進行的。代碼實現:

/**
 * @Author: 王琦 <QQ.Eamil>[email protected]</QQ.Eamil>
 * @Date: 2019-5-4 0004 9:43
 * @Description: 倒計時器示例:火箭發射
 */
public class CountDownLatchDemo implements Runnable{

    static final CountDownLatch latch = new CountDownLatch(10);
    static final CountDownLatchDemo demo = new CountDownLatchDemo();

    @Override
    public void run() {
        // 模擬檢查任務
        try {
            Thread.sleep(new Random().nextInt(10) * 1000);
            System.out.println("check complete");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //計數減一
            //放在finally避免任務執行過程出現異常,導致countDown()不能被執行
            latch.countDown();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for (int i=0; i<10; i++){
            exec.submit(demo);
        }

        // 等待檢查
        latch.await();

        // 發射火箭
        System.out.println("Fire!");
        // 關閉線程池
        exec.shutdown();
    }
}

上述代碼中我們先生成了一個CountDownLatch實例。計數數量爲10,這表示需要有10個線程來完成任務,等待在CountDownLatch上的線程才能繼續執行。latch.countDown();方法作用是通知CountDownLatch有一個線程已經準備完畢,倒計數器可以減一了。latch.await()方法要求主線程等待所有10個檢查任務全部準備好才一起並行執行。

示例二、

CountDownLatch是java.util.concurrent包中一個類,CountDownLatch只要提供的機制是多個(具體數量等於初始化CountDownLatch時count的值)線程都達到了預期狀態或者完成了預期工作時觸發事件,其他線程可以等待這個事件來觸發自己後續的工作。等待的線程可以是多個,即CountDownLatch可以喚醒多個等待的線程。到達自己預期狀態的線程會調用CountDownLatch的countDown方法,而等待的線程會調用CountDownLatch的await方法。
在這裏插入圖片描述
結合以下例子,可以快速掌握這個這個類的基本使用方法:
例2.1:
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDown = new CountDownLatch(1);
CountDownLatch await = new CountDownLatch(5);

        // 依次創建並啓動處於等待狀態的5個MyRunnable線程
        for (int i = 0; i < 5; ++i) {
            new Thread(new MyRunnable(countDown, await)).start();
        }

        System.out.println("用於觸發處於等待狀態的線程開始工作......");
        System.out.println("用於觸發處於等待狀態的線程工作完成,等待狀態線程開始工作......");
        countDown.countDown();
        await.await();
        System.out.println("Bingo!");
}

public class MyRunnable implements Runnable {

    private final CountDownLatch countDown;
    private final CountDownLatch await;

    public MyRunnable(CountDownLatch countDown, CountDownLatch await) {
        this.countDown = countDown;
        this.await = await;
    }

    public void run() {
        try {
            countDown.await();//等待主線程執行完畢,獲得開始執行信號...
            System.out.println("處於等待的線程開始自己預期工作......");
            await.countDown();//完成預期工作,發出完成信號...
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
運行結果:
用於觸發處於等待狀態的線程開始工作......
用於觸發處於等待狀態的線程工作完成,等待狀態線程開始工作......
處於等待的線程開始自己預期工作......
處於等待的線程開始自己預期工作......
處於等待的線程開始自己預期工作......
處於等待的線程開始自己預期工作......
處於等待的線程開始自己預期工作......
Bingo!

例2.2:

public class TestDemo2 {

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
        int count = 10;
        final CountDownLatch latch = new CountDownLatch(count);

        for (int i = 0; i < count; i++) {
            threadPool.execute(new MyRunnable1(latch, i));
        }

        latch.await();
        System.err.println("等待線程被喚醒!");
        threadPool.shutdown();
    }
}

class MyRunnable1 implements Runnable {

    CountDownLatch latch = null;
    int i;

    public MyRunnable1(CountDownLatch latch, int i) {
        this.latch = latch;
        this.i = i;
    }

    @Override
    public void run() {
        System.err.println("線程" + i +"完成了操作...");
        try {
            Thread.currentThread();
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        latch.countDown();
    }

}
運行結果:
線程0完成了操作...
線程3完成了操作...
線程2完成了操作...
線程1完成了操作...
線程4完成了操作...//暫停4秒
線程5完成了操作...
線程6完成了操作...
線程8完成了操作...
線程7完成了操作...
線程9完成了操作...//暫停4秒
等待線程被喚醒!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章