天天看點

并發工具 CountDownLatch

看看這個部落格 更有助于了解閉鎖

CountDownLatch:閉鎖

基本原理

工作原理,如下圖。

并發工具 CountDownLatch

線程TA調用await()方法後便會挂起,T1,T2,T3依次調用countDown()方法進行對cnt進行減1操作,最後count值為0時 ,挂起的線程TA便會被喚醒。

主要的方法

  1. CountDownLatch(int count) :僅有的一個構造函數,參數count為需要倒數的數值;
  2. await():調用該方法的線程會被挂起,直到count為0時,該線程才會繼續執行;
  3. countDown():對count數值進行減1操作。

應用場景1:一等多

一個線程等待多個線程執行完才繼續自己的操作;可以使用閉鎖來保證某些活動到其他活動做完才繼續進行。

  1. 確定計算機在其需要的所有資源被初始化之後才繼續執行;
  2. 確定某個服務在其依賴的所有其他服務都已經啟動之後才啟動;
  3. 等待直到某個操作所有參與者都準備就緒了繼續操作。

demo1

package com.mark.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * author:Mark
 * date:2018/7/31  17:46
 */
@Slf4j
public class CountDownLatchTest1 {
    private final static int threadCount = 20;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);//閉鎖
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            executorService.execute(()->{
                try {
                    test(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        log.info("finish");
        executorService.shutdown();
    }

    private static void test(int threadNum) throws InterruptedException {
        Thread.sleep(100);
        log.info("{}", threadNum);
        Thread.sleep(100);
    }
}

           

運作結果

22:56:26.945 [pool-1-thread-19] INFO com.mark.example.aqs.CountDownLatchTest1 - 18

22:56:26.945 [pool-1-thread-8] INFO com.mark.example.aqs.CountDownLatchTest1 - 7

22:56:26.945 [pool-1-thread-12] INFO com.mark.example.aqs.CountDownLatchTest1 - 11

22:56:26.945 [pool-1-thread-9] INFO com.mark.example.aqs.CountDownLatchTest1 - 8

22:56:26.945 [pool-1-thread-15] INFO com.mark.example.aqs.CountDownLatchTest1 - 14

22:56:26.945 [pool-1-thread-13] INFO com.mark.example.aqs.CountDownLatchTest1 - 12

22:56:26.946 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest1 - 0

22:56:26.945 [pool-1-thread-10] INFO com.mark.example.aqs.CountDownLatchTest1 - 9

22:56:26.945 [pool-1-thread-18] INFO com.mark.example.aqs.CountDownLatchTest1 - 17

22:56:26.945 [pool-1-thread-11] INFO com.mark.example.aqs.CountDownLatchTest1 - 10

22:56:26.946 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest1 - 2

22:56:26.946 [pool-1-thread-17] INFO com.mark.example.aqs.CountDownLatchTest1 - 16

22:56:26.945 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest1 - 1

22:56:26.946 [pool-1-thread-20] INFO com.mark.example.aqs.CountDownLatchTest1 - 19

22:56:26.945 [pool-1-thread-14] INFO com.mark.example.aqs.CountDownLatchTest1 - 13

22:56:26.945 [pool-1-thread-7] INFO com.mark.example.aqs.CountDownLatchTest1 - 6

22:56:26.946 [pool-1-thread-6] INFO com.mark.example.aqs.CountDownLatchTest1 - 5

22:56:26.946 [pool-1-thread-16] INFO com.mark.example.aqs.CountDownLatchTest1 - 15

22:56:26.946 [pool-1-thread-4] INFO com.mark.example.aqs.CountDownLatchTest1 - 3

22:56:26.946 [pool-1-thread-5] INFO com.mark.example.aqs.CountDownLatchTest1 - 4

22:56:27.051 [main] INFO com.mark.example.aqs.CountDownLatchTest1 - finish

運作結果分析:程式中聲明了一個大小為20的CountDownLatch執行個體,線上程池中運作20個線程的時候。等到這20個線程全部執行完後再執行

log.info("finish");

。一等多的展現,main線程等待其他的線程之執行完。才繼續往下走。

demo2 :顯示聲明等待時間

package com.mark.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * author:Mark
 * date:2018/7/31  17:46
 */
@Slf4j
public class CountDownLatchTest2 {
    private final static int threadCount = 10;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);//閉鎖
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            executorService.execute(()->{
                try {
                    test(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await(20, TimeUnit.MILLISECONDS);//指定時間喚醒--》注意列印finish語句
        log.info("finish");
        executorService.shutdown();//不會立即停止線程池裡的線程。而是等待線程池裡的線程執行完再關閉線程池
    }

    private static void test(int threadNum) throws InterruptedException {
        Thread.sleep(10);
        log.info("{}", threadNum);
    }
}


           

運作結果:

23:01:28.254 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 0

23:01:28.254 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 2

23:01:28.254 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 1

23:01:28.263 [main] INFO com.mark.example.aqs.CountDownLatchTest2 - finish

23:01:28.269 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 4

23:01:28.269 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 5

23:01:28.269 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 3

23:01:28.280 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 6

23:01:28.280 [pool-1-thread-1] INFO com.mark.example.aqs.CountDownLatchTest2 - 8

23:01:28.280 [pool-1-thread-2] INFO com.mark.example.aqs.CountDownLatchTest2 - 7

23:01:28.291 [pool-1-thread-3] INFO com.mark.example.aqs.CountDownLatchTest2 - 9

應用場景2:多等一

package com.mark.learning.concurrent2.flowcontrol.countdownlatch;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description: 多等一,假設 5個使用者等待發紅包。
 * @Author: Mark
 * @CreateDate: 2020/2/2 23:29
 * @Version: 1.0
 * @Copyright : 豆漿油條個人非正式工作室
 */
@Slf4j
public class CountDownLatchDemo2_1 {

    private final static int THREAD_COUNT = 5;
    private static CountDownLatch BEGIN_CNT = new CountDownLatch(1);
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);

    public static void main(String[] args) throws InterruptedException {

        //起五個線程模拟五個使用者,等待紅包、搶紅包
        for (int i = 0; i < THREAD_COUNT; i++) {
            int finalI = i+1;
            executorService.execute(() -> {
                try {
                    log.info("使用者" + finalI + "打開《我愛我家》群聊,等待群主發紅包!!");
                    BEGIN_CNT.await();
                    log.info("使用者" + finalI + "開始搶紅包!!!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        //群主發紅包
        Thread.sleep(5000);
        BEGIN_CNT.countDown();
        executorService.shutdown();
    }

}

           

運作結果

23:48:32.259 [pool-1-thread-2] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者2打開《我愛我家》群聊,等待群主發紅包!!
23:48:32.259 [pool-1-thread-5] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者5打開《我愛我家》群聊,等待群主發紅包!!
23:48:32.259 [pool-1-thread-4] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者4打開《我愛我家》群聊,等待群主發紅包!!
23:48:32.259 [pool-1-thread-1] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者1打開《我愛我家》群聊,等待群主發紅包!!
23:48:32.259 [pool-1-thread-3] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者3打開《我愛我家》群聊,等待群主發紅包!!
23:48:37.253 [pool-1-thread-4] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者4開始搶紅包!!!
23:48:37.253 [pool-1-thread-2] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者2開始搶紅包!!!
23:48:37.253 [pool-1-thread-1] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者1開始搶紅包!!!
23:48:37.253 [pool-1-thread-3] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者3開始搶紅包!!!
23:48:37.253 [pool-1-thread-5] INFO com.mark.learning.concurrent2.flowcontrol.countdownlatch.CountDownLatchDemo2_1 - 使用者5開始搶紅包!!!
           

總結

  1. CountDownLatch不能夠重用,如果需要重新計數,可以考慮用CyclicBarrier或者建構新的CountDownLatch執行個體;
  2. CountDownLatch執行個體時,需要傳遞參數(倒數次數),倒數到0的時候,調用await()方法才會繼續執行;

繼續閱讀