循環(huán)柵欄 CyclicBarrier
1.前言
本節(jié)帶領(lǐng)大家認識第三個常用的 Java 并發(fā)工具類之 CyclicBarrier。
本節(jié)先介紹 CyclicBarrier 工具類的表達的概念和最基本用法,接著通過一個生活中的例子為大家解釋 CyclicBarrier 工具類的使用場合,然后通過簡單的編碼實現(xiàn)此場景,最后帶領(lǐng)大家熟悉 CyclicBarrier 工具類的其他重要方法。
下面我們正式開始介紹吧。
2.概念解釋
所謂 Cyclic 即循環(huán)的意思,所謂 Barrier 即屏障的意思。所以綜合起來,CyclicBarrier 指的就是循環(huán)屏障,雖然這個叫法很奇怪,但是卻能很好地表達其含義。
CyclicBarrier 工具類允許一組線程相互等待,直到所有線程都到達一個公共的屏障點,然后這些線程一起繼續(xù)執(zhí)行后繼邏輯。之所以稱之為 “循環(huán)”,是因為在所有線程都釋放了對這個屏障的使用后,這個屏障還可以重新使用。我們通過一張圖可以直觀了解其表達的控制模型。
現(xiàn)在我們已經(jīng)了解了基本概念邏輯,CyclicBarrier 工具類最基本的用法是怎樣的呢?看下面。
3.基本用法
// 創(chuàng)建一個 CyclicBarrier 對象,初始化相互等待的線程數(shù)量
CyclicBarrier cyclicBarrier = new CyclicBarrier(線程個數(shù));
// 線程1開始處理邏輯
...
// 線程1等待其他線程執(zhí)行到屏障點
cyclicBarrier.await();
// 線程1等到了其他所有線程達到屏障點后繼續(xù)處理后繼邏輯
...
// 線程n開始處理邏輯
...
// 線程n等待其他線程執(zhí)行到屏障點
cyclicBarrier.await();
// 線程n等到了其他所有線程達到屏障點后繼續(xù)處理后繼邏輯
...
是不是很簡單,CyclicBarrier 應(yīng)用在哪些場合比較合適呢?下面我們給出最常用的場景說明。
4.常用場景
CyclicBarrier 最適合一個由多個線程共同協(xié)作完成任務(wù)的場合。
這樣描述很抽象,我們還是舉一個生活中的例子說明:某學(xué)習(xí)班總共 5 位同學(xué),約定周末一起乘坐大巴出游,約定了共同的集合地點,雇傭了 1 位司機。請看下面代碼。
5.場景案例
import lombok.SneakyThrows;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
// 創(chuàng)建一個 Runnable 對象,用于屏障解除時處理全局邏輯,在此例子中代表大巴司機
private static Runnable driver = new Runnable() {
public void run() {
System.out.println("所有同學(xué)已經(jīng)集合完畢,開始啟動車輛出發(fā)。");
}
};
// 創(chuàng)建一個 CyclicBarrier 對象,初始化為 5, 代表需要控制同步的線程個數(shù),在此例子中代表 5 位同學(xué)
static int threadCount = 5;
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount, driver);
public static void main(String[] args) throws InterruptedException {
// 模擬同學(xué)
for(int i=1; i<=threadCount; i++) {
// 模擬某個同學(xué)的動作
new Thread(new Runnable() {
@SneakyThrows
public void run() {
System.out.println( Thread.currentThread().getName() + "已經(jīng)開始出門...");
// 模擬同學(xué)出門趕往集合點的用時
try {
Thread.sleep(new Random().nextInt(10000));
} catch (Exception e) {}
System.out.println( Thread.currentThread().getName() + "已經(jīng)到達集合點");
// 等待其他同學(xué)到達集合點(等待其他線程到達屏障點)
cyclicBarrier.await();
}
}, i + "號同學(xué)").start();
}
}
}
運行上面代碼,我們觀察一下運行結(jié)果。
1號同學(xué)準(zhǔn)備出門...
2號同學(xué)準(zhǔn)備出門...
3號同學(xué)準(zhǔn)備出門...
4號同學(xué)準(zhǔn)備出門...
5號同學(xué)準(zhǔn)備出門...
5號同學(xué)已經(jīng)到達集合點
4號同學(xué)已經(jīng)到達集合點
1號同學(xué)已經(jīng)到達集合點
2號同學(xué)已經(jīng)到達集合點
3號同學(xué)已經(jīng)到達集合點
所有同學(xué)已經(jīng)集合完畢,開始啟動車輛出發(fā)。
觀察結(jié)果,和我們的預(yù)期一致。注意體會 CyclicBarrier 提供的多線程共同協(xié)作的模型。
6.其他方法介紹
除過上面代碼中使用的最基本的 await()方法之外,還有下面幾個方法大家可以了解一下。
-
CyclicBarrier(int parties)
相比案例中使用的 CyclicBarrier(int parties, Runnable barrierAction) 構(gòu)造方法,此方法只用于控制并發(fā)線程,不做屏障點到達后的其他動作。 -
await(long timeout, TimeUnit unit) 方法
此方法可以設(shè)置等待的時限,當(dāng)時限過后還未被喚起,則直接自行喚醒繼續(xù)執(zhí)行后繼任務(wù)。 -
getNumberWaiting() 方法
調(diào)用此方法,可以獲得當(dāng)前還在等待屏障點解除的線程數(shù),一般用于了解整體處理進度。
7.小結(jié)
本節(jié)通過一個簡單的例子,介紹了 CyclicBarrier 相關(guān)的概念原理,使用場景和基本用法。希望大家在學(xué)習(xí)過程中,多思考勤練習(xí),早日掌握之。