我數 10 下大家一起上
在日常編碼中,Java 并發(fā)編程可是少不了,試試下面這些并發(fā)編程工具類:
今天先帶領大家重溫學習 CountDownLatch 這個牛叉的工具類,肝起。
認識 CountDownLatch
CountDownLatch是一個同步工具類,用來協調多個線程之間的同步,或者說起到線程之間通信的作用(非互斥)。
CountDownLatch 能夠使一個線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。使用一個計數器進行實現。計數器初始值為線程的數量。當每一個線程完成自己任務后,計數器的值就會減一。當計數器的值為0時,表示所有的線程都已經完成一些任務,然后在CountDownLatch上等待的線程就可以恢復執(zhí)行接下來的任務。
CountDownLatch 的使用
CountDownLatch類使用起來非常簡單。
Class 位于:java.util.concurrent.CountDownLatch
下面簡單介紹它的構造方法和常用方法。
構造方法
CountDownLatch只提供了一個構造方法:
- // count 為初始計數值
- public CountDownLatch(int count) {
- // ……
- }
常用方法
//常用方法1:調用await()方法的線程會被掛起,它會等待直到count值為0才繼續(xù)執(zhí)行
- //常用方法1:調用await()方法的線程會被掛起,它會等待直到count值為0才繼續(xù)執(zhí)行
- public void await() throws InterruptedException {
- // ……
- }
- // 常用方法2:和await()類似,只不過等待超時后count值還沒變?yōu)?的話就會繼續(xù)執(zhí)行
- public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
- // ……
- }
- // 常用方法3:將count值減1
- public void countDown() {
- // ……
- }
CountDownLatch 的應用場景
我們考慮一個場景:用戶購買一個商品下單成功后,我們會給用戶發(fā)送各種消息提示用戶『購買成功』,比如發(fā)送郵件、微信消息、短信等。所有的消息都發(fā)送成功后,我們在后臺記錄一條消息表示成功。
當然我們可以使用單線程去完成,逐個完成每個操作,如下圖所示:
但是這樣效率就會非常低。如何解決單線程效率低的問題?當然是通過多線程啦。
使用多線程也會遇到一個問題,子線程消息還沒發(fā)送完,主線程可能就已經打出『所有的消息都已經發(fā)送完畢啦』,這在邏輯上肯定是不對的。我們期望所有子線程發(fā)完消息主線程才會打印消息,怎么實現呢?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));
- // 主線程阻塞,等待所有子線程發(fā)完消息
- countDownLatch.await();
- // 所有子線程已經發(fā)完消息,計數器為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 {
- // 線程發(fā)送消息
- System.out.println("Send " + messageName);
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- } finally {
- // 發(fā)完消息計數器減 1
- countDownLatch.countDown();
- }
- }
- }
- }
程序運行結果:
- main thread: Success to place an order
- Send email
- Send wechat
- Send sms
- main thread: all message has been sent
從運行結果可以看到主線程是在所有的子線程發(fā)送完消息后才打印,這符合我們的預期。
CountDownLatch 的限制
CountDownLatch是一次性的,計算器的值只能在構造方法中初始化一次,之后沒有任何機制再次對其設置值,當CountDownLatch使用完畢后,它不能再次被使用。