《死磕 Java 并發程式設計》系列連載中,大家可以關注一波:
👍🏻『死磕Java并發程式設計系列』 01 十張圖告訴你多線程那些破事
『死磕Java并發程式設計系列』 02 面試官:說說什麼是Java記憶體模型?
『死磕Java并發程式設計系列』 03 面試必問的CAS原理你會了嗎?
『死磕Java并發程式設計系列』 04 面試官:說說Atomic原子類的實作原理?
👍🏻『死磕Java并發程式設計系列』 05 圖解Java中那18 把鎖
在日常編碼中,Java 并發程式設計可是少不了,試試下面這些并發程式設計工具類。
今天先帶領大家一起重溫學習 CountDownLatch 這個牛叉的工具類。
認識 CountDownLatch
CountDownLatch
是一個同步工具類,用來協調多個線程之間的同步,或者說起到線程之間通信的作用(非互斥)。
CountDownLatch 能夠使一個線程在等待另外一些線程完成各自工作之後,再繼續執行。使用一個計數器進行實作。計數器初始值為線程的數量。當每一個線程完成自己任務後,計數器的值就會減一。當計數器的值為0時,表示所有的線程都已經完成一些任務,然後在CountDownLatch上等待的線程就可以恢複執行接下來的任務。

CountDownLatch 的使用
CountDownLatch類使用起來非常簡單。
Class 位于:
java.util.concurrent.CountDownLatch
下面簡單介紹它的構造方法和常用方法。
構造方法
CountDownLatch隻提供了一個構造方法:
// count 為初始計數值
public CountDownLatch(int count) {
// ……
}
常用方法
//常用方法1:調用await()方法的線程會被挂起,它會等待直到count值為0才繼續執行
public void await() throws InterruptedException {
// ……
}
// 常用方法2:和await()類似,隻不過等待逾時後count值還沒變為0的話就會繼續執行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
// ……
}
// 常用方法3:将count值減1
public void countDown() {
// ……
}
CountDownLatch 的應用場景
我們考慮一個場景:使用者購買一個商品下單成功後,我們會給使用者發送各種消息提示使用者『購買成功』,比如發送郵件、微信消息、短信等。所有的消息都發送成功後,我們在背景記錄一條消息表示成功。
當然我們可以使用單線程去完成,逐個完成每個操作,如下圖所示:
但是這樣效率就會非常低。如何解決單線程效率低的問題?當然是通過多線程啦。
使用多線程也會遇到一個問題,子線程消息還沒發送完,主線程可能就已經打出『所有的消息都已經發送完畢啦』,這在邏輯上肯定是不對的。我們期望所有子線程發完消息主線程才會列印消息,怎麼實作呢?CountDownLatch就可以解決這一類問題。
我們使用代碼實作上面的需求。
import java.util.concurrent.*;
public class OrderServiceDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("main thread: Success to place an order");
int count = 3;
CountDownLatch countDownLatch = new CountDownLatch(count);
Executor executor = Executors.newFixedThreadPool(count);
executor.execute(new MessageTask("email", countDownLatch));
executor.execute(new MessageTask("wechat", countDownLatch));
executor.execute(new MessageTask("sms", countDownLatch));
// 主線程阻塞,等待所有子線程發完消息
countDownLatch.await();
// 所有子線程已經發完消息,計數器為0,主線程恢複
System.out.println("main thread: all message has been sent");
}
static class MessageTask implements Runnable {
private String messageName;
private CountDownLatch countDownLatch;
public MessageTask(String messageName, CountDownLatch countDownLatch) {
this.messageName = messageName;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
// 線程發送消息
System.out.println("Send " + messageName);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
// 發完消息計數器減 1
countDownLatch.countDown();
}
}
}
}
程式運作結果:
main thread: Success to place an order
Send email
Send wechat
Send sms
main thread: all message has been sent
從運作結果可以看到主線程是在所有的子線程發送完消息後才列印,這符合我們的預期。
CountDownLatch 的限制
CountDownLatch是一次性的,電腦的值隻能在構造方法中初始化一次,之後沒有任何機制再次對其設定值,當CountDownLatch使用完畢後,它不能再次被使用。
大家學會了麼?後面會接着講剩餘的幾種并發工具類,拭目以待吧~
我是雷小帥,愛了~