看看這個部落格 更有助于了解閉鎖
CountDownLatch:閉鎖
基本原理
工作原理,如下圖。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn5UMjRUT3VFRapGbzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuYzM3EzMxEjMyAjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
線程TA調用await()方法後便會挂起,T1,T2,T3依次調用countDown()方法進行對cnt進行減1操作,最後count值為0時 ,挂起的線程TA便會被喚醒。
主要的方法
- CountDownLatch(int count) :僅有的一個構造函數,參數count為需要倒數的數值;
- await():調用該方法的線程會被挂起,直到count為0時,該線程才會繼續執行;
- countDown():對count數值進行減1操作。
應用場景1:一等多
一個線程等待多個線程執行完才繼續自己的操作;可以使用閉鎖來保證某些活動到其他活動做完才繼續進行。
- 確定計算機在其需要的所有資源被初始化之後才繼續執行;
- 確定某個服務在其依賴的所有其他服務都已經啟動之後才啟動;
- 等待直到某個操作所有參與者都準備就緒了繼續操作。
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開始搶紅包!!!
總結
- CountDownLatch不能夠重用,如果需要重新計數,可以考慮用CyclicBarrier或者建構新的CountDownLatch執行個體;
- CountDownLatch執行個體時,需要傳遞參數(倒數次數),倒數到0的時候,調用await()方法才會繼續執行;