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



















 
 
 



 
 
 
 