同步計數(shù)器 CountDownLatch
1. 前言
本節(jié)帶領(lǐng)大家認識第二個常用的 Java 并發(fā)工具類之 CountDownLatch。
本節(jié)先介紹 CountDownLatch 工具類表達的概念和最基本用法,接著通過一個生活中的例子為大家解釋 CountDownLatch 工具類的使用場合,然后通過簡單的編碼實現(xiàn)此場景,最后帶領(lǐng)大家熟悉 CountDownLatch 工具類的其他重要方法。
下面我們正式開始介紹吧。
2. 概念解釋
CountDownLatch 工具類從字面理解為 “倒計數(shù)鎖”,其內(nèi)部使用一個計數(shù)器進行實現(xiàn),計數(shù)器初始值為線程的數(shù)量。當每一個線程完成自己的任務(wù)后,計數(shù)器的值就會減一。當計數(shù)器的值為 0 時,表示所有的線程都已經(jīng)完成了任務(wù),然后在 CountDownLatch 上等待的線程就可以恢復繼續(xù)執(zhí)行后繼任務(wù)。是不是很抽象,其實很簡單,看下面的圖例。
這就是 CountDownLatch 工具類的基本邏輯。概念已經(jīng)了解了,CountDownLatch 工具類最基本的用法是怎樣的呢?看下面。
3. 基本用法
// 創(chuàng)建一個 CountDownLatch 對象
CountDownLatch countDownLatch = new CountDownLatch(子線程個數(shù));
// 子線程1開始處理邏輯
...
// 子線程執(zhí)行完所有邏輯進行計數(shù)器減1
countDownLatch.countDown();
// 子線程n開始處理邏輯
...
// 子線程執(zhí)行完所有邏輯進行計數(shù)器減1
countDownLatch.countDown();
// 主線程等待所有子線程執(zhí)行完
countDownLatch.await();
// 主線程繼續(xù)執(zhí)行后繼邏輯
...
是不是很簡單,CountDownLatch 應(yīng)用在哪些場合比較合適呢?下面我們給出最常用的場景說明。
4. 常用場景
CountDownLatch 經(jīng)常用于某一線程在開始運行前等待其他關(guān)聯(lián)線程執(zhí)行完畢的場合。
比如我們制作一張復雜報表,報表的各部分可以安排對應(yīng)的一個線程進行計算,只有當所有線程都執(zhí)行完畢后,再由最終的報表輸出線程進行報表文件生成。
下面我們使用 CountDownLatch 實現(xiàn)這個例子。假設(shè)這張報表有 5 個部分,我們總共安排 5 個子線程分別計算,再設(shè)置 1 個報表輸出線程用于最終生成報表文件。請看下面代碼。
5. 場景案例
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
// 創(chuàng)建一個 CountDownLatch 對象,初始化為 5, 代表需要控制同步的子線程個數(shù)
static int threadCount = 5;
private static CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 報表生成主線程
public static void main(String[] args) throws InterruptedException {
// 定義報表子線程
for(int i=1; i<=threadCount; i++) {
// 開始報表子線程處理
new Thread(new Runnable() {
public void run() {
// 模擬報表數(shù)據(jù)計算時間
try {
Thread.sleep(new Random().nextInt(5000));
} catch (Exception e) {}
System.out.println( Thread.currentThread().getName() + "已經(jīng)處理完畢");
countDownLatch.countDown();
}
}, "報表子線程" + i).start();
}
// 主線程等待所有子線程運行完畢后輸出報表文件
countDownLatch.await();
System.out.println("報表數(shù)據(jù)已經(jīng)全部計算完畢,開始生成報表文件...");
}
}
運行上面代碼,我們觀察一下運行結(jié)果。
報表子線程3已經(jīng)處理完畢
報表子線程5已經(jīng)處理完畢
報表子線程1已經(jīng)處理完畢
報表子線程4已經(jīng)處理完畢
報表子線程2已經(jīng)處理完畢
報表數(shù)據(jù)已經(jīng)全部計算完畢,開始生成報表文件...
觀察結(jié)果,和我們的預期一致。注意體會 CountDownLatch 提供的多線程共同協(xié)作的模型。
6. 其他方法介紹
除過上面代碼中使用的最基本的 countDown ()、await () 方法之外,還有兩個方法大家可以了解一下。
-
await (long, TimeUnit) 方法
此方法提供了更靈活的等待參數(shù),可以設(shè)置等待超時時間。當?shù)却^了設(shè)定的時限,則不再阻塞直接開始后繼處理。 -
getCount () 方法
調(diào)用此方法,可獲得當前計數(shù)器的數(shù)值,了解整體處理進度。
7. 小結(jié)
本節(jié)解釋了 CountDownLatch 的基本邏輯模型,且通過一個簡單的例子,介紹了 CountDownLatch 的使用場景和基本用法。希望大家在學習過程中,多思考勤練習,早日掌握之。