作者 : 畢來生
前言
在 java.util.concurrent 包中提供了多種并發容器類來改進同步容器 的性能。今天我們來聊一聊CountDownLatch 的使用場景。看看它到底是怎麼玩耍的。
CountDownLatch 是幹什麼的?
CountDownLatch 一個同步輔助類,在完成一組正在其他線程中執行的操作 之前,它允許一個或多個線程一直等待。
CountDownLatch原理
CountDownLatch是通過一個計數器來實作的,計數器的初始化值為線程的數量。每當一個線程完成了自己的任務後,計數器的值就相應得減1。當計數器到達0時,表示所有的線程都已完成任務,然後在閉鎖上等待的線程就可以恢複執行任務。
使用場景
在某些業務情況下,要求我們等某個條件或者任務完成後才可以繼續處理後續任務。同時線上程完成時也會觸發一定事件。友善業務繼續向下執行。這個時候我們的CountDownLatch就隆重登場啦。常用核心方法為
/**
* 遞減計數器的計數,如果計數達到0,則釋放所有等待的線程。
* 如果目前計數大于0,則将計數減少。
* 如果新的計數為0,會重新開機所有等待線程
* 如果目前計數等于0,則不做任何操作
**/
public void countDown() {
sync.releaseShared(1);
}
/**
* 1、目前線程在倒數至0之前會一直等待
* 2、除非線程被中斷或超出了指定的時間。
* 3、如果目前計數為0,立刻傳回為true
* 4、如果目前計數等于0,則不做任何操作
* 5、如果目前線程在進入此方法時已經設定了該線程的中斷狀态,
* 或者在等待時被中斷,則抛出InterruptedException
**/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
實戰演練
package org.bilaisheng.juc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: bilaisheng
* @Wechat: 878799579
* @Date: 2019/1/2 23:30
* @Todo: CountDownLatch demo。
* @Version : JDK11 , IDEA2018
*/
public class CountDownLatchTest {
// 舉個例子:有十個毒販在和警察激戰
public static void main(String[] args) throws InterruptedException {
// 開始計數器
final CountDownLatch start = new CountDownLatch(1);
// 結束計數器
final CountDownLatch end = new CountDownLatch(10);
final ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 1; i <= 10; i++) {
// 這個變量純粹為了掩飾到底哪個倒黴先被幹掉。
final String criminal = "第"+i+"個毒販";
Runnable task = new Runnable() {
@Override
public void run() {
try {
// 如果目前已經沒有可以毒販,立刻傳回
start.await();
Thread.sleep(500);
System.out.println(criminal + "被幹掉了!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 幹掉一個。毒販活着的數量就減1
end.countDown();
}
}
};
executorService.submit(task);
}
// 抓捕開始
start.countDown();
// 全都被幹掉了(計數器為0)
end.await();
// 任務完成:收工回家
executorService.shutdown();
}
}

閉鎖可以延遲線程的進度直到其到達終止狀态,閉鎖可以用來確定某些活動直到其他活動都完成才繼續執行:
- 確定某個計算在其需要的所有資源都被初始化之後才繼續執行;
- 確定某個服務在其依賴的所有其他服務都已經啟動之後才啟動;
- 等待直到某個操作所有參與者都準備就緒再繼續執行。