Java多線程之CyclicBarrier
這里就JUC包中的CyclicBarrier類做相關(guān)介紹

概述
JUC中的CyclicBarrier類是一個并發(fā)控制工具。其可以使線程在柵欄處進(jìn)行等待。當(dāng)指定數(shù)量的線程全部到達(dá)柵欄處后柵欄才會打開,從而使各線程結(jié)束阻塞繼續(xù)向下執(zhí)行。其主要方法如下所示,可以看到在線程全部到達(dá)柵欄時,還可以通過barrierAction參數(shù)設(shè)置準(zhǔn)備打開柵欄前需執(zhí)行的任務(wù)。其中,該任務(wù)由最后一個到達(dá)柵欄的線程負(fù)責(zé)執(zhí)行。具體地,線程調(diào)用await方法實現(xiàn)告訴CyclicBarrier自己已經(jīng)到達(dá)柵欄處,并阻塞等待柵欄打開
//?創(chuàng)建一個指定計數(shù)器值的CyclicBarrier實例
public?CyclicBarrier(int?parties);
//?創(chuàng)建一個指定計數(shù)器值的CyclicBarrier實例,?并指定柵欄打開前需執(zhí)行的任務(wù)
public?CyclicBarrier(int?parties,?Runnable?barrierAction);
//?線程阻塞等待柵欄打開
public?int?await()?throws?InterruptedException,?BrokenBarrierException;
//?支持超時的await方法
public?int?await(long?timeout,?TimeUnit?unit)?throws?InterruptedException,?BrokenBarrierException,?TimeoutException;
//?喚醒其他正在柵欄處被阻塞的線程(即拋出BrokenBarrierException異常),?同時將CyclicBarrier實例恢復(fù)為初始化狀態(tài),以便下一次使用
public?void?reset();
基本實踐
下面即是一個CyclicBarrier的基本實踐示例
public?class?CyclicBarrierTest1?{
????private?static?DateTimeFormatter?formatter?=?DateTimeFormatter.ofPattern("HH:mm:ss");
????@Test
????public?void?test1()?throws?InterruptedException?{
????????ExecutorService?threadPool?=?Executors.newFixedThreadPool(10);
????????Runnable?initTask?=?()?->?{
????????????info("---------------------------------");
????????};
????????CyclicBarrier?cyclicBarrier?=?new?CyclicBarrier(3,?initTask);
????????Stream.of("張三","李四","王二")
????????????.map(?name?->?new?PlayGame(name,?cyclicBarrier)?)
????????????.forEach(playGame?->?{
????????????????threadPool.execute(playGame);
????????????}?);
????????//?主線程等待所有任務(wù)執(zhí)行完畢
????????try{?Thread.sleep(?120*1000?);?}?catch?(Exception?e)?{}
????????info("Game?Over");
????}
????/**
?????*?打印信息
?????*?@param?msg
?????*/
????private?static?void?info(String?msg)?{
????????String?time?=?formatter.format(LocalTime.now());
????????String?threadName?=?Thread.currentThread().getName();
????????String?log?=?"["+time+"]?"+?msg+"?<"+threadName+">";
????????System.out.println(log);
????}
????/**
?????*?模擬業(yè)務(wù)耗時
?????*/
????private?static?void?doSomeWork()?{
????????try{
????????????Integer?second?=?RandomUtils.nextInt(3,20);
????????????System.out.println("second:?"?+?second);
????????????Thread.sleep(?second?*?1000?);
????????}catch?(Exception?e)?{
????????????System.out.println(?"Happen?Exception:?"?+?e.getMessage());
????????}
????}
????@AllArgsConstructor
????private?static?class?PlayGame?implements?Runnable{
????????private?String?name;
????????private?CyclicBarrier?cyclicBarrier;
????????@Override
????????public?void?run()?{
????????????//?模擬業(yè)務(wù)耗時
????????????doSomeWork();
????????????info(name?+?"?上線");
????????????//?阻塞等待其他玩家上線
????????????try{
????????????????cyclicBarrier.await();
????????????}catch?(Exception?e)?{
????????????????System.out.println(?"Happen?Exception:?"?+?e);
????????????}
????????????info(name?+?"?選擇角色?開始");
????????????//?模擬業(yè)務(wù)耗時
????????????doSomeWork();
????????????info(name?+?"?選擇角色?結(jié)束");
????????????//?阻塞等待其他玩家選擇角色
????????????try{
????????????????cyclicBarrier.await();
????????????}catch?(Exception?e)?{
????????????????System.out.println(?"Happen?Exception:?"?+?e);
????????????}
????????????info(name?+?"?開始游戲");
????????}
????}
}
從測試結(jié)果可以看出,當(dāng)用戶 開始選擇角色 或 開始游戲時,各線程是同時開始的。至此也可以看出其與CountDownLatch的顯著區(qū)別,后者是一次性的,而前者CyclicBarrier則可以重復(fù)使用

基本原理
通過上面的代碼示例,可以看到CyclicBarrier與CountDownLatch相比功能很類似。只不過前者可以重復(fù)使用,而后者則是一次性的。但二者在實現(xiàn)上卻大相徑庭,CountDownLatch是直接基于AQS實現(xiàn)的。而CyclicBarrier則是利用ReentrantLock、Condition進(jìn)行實現(xiàn)的。具體地,當(dāng)線程調(diào)用CyclicBarrier的await方法時,如果未達(dá)到指定數(shù)量時,則是通過Condition條件變量的await方法進(jìn)行阻塞的;如果是最后一個線程則會通過Condition條件變量的signalAll方法來喚醒所有被阻塞的線程
與此同時,由于CyclicBarrier是可重復(fù)使用的。故每一輪結(jié)束后,其內(nèi)部會通過nextGeneration方法生成所謂的下一代CyclicBarrier。本質(zhì)上相當(dāng)于重新實例化了一次CyclicBarrier
Note
在實際使用CyclicBarrier過程中,需要非常小心處理BrokenBarrierException異常。本文示例代碼為了簡便,故省略了異常處理過程。因為發(fā)生該異常說明柵欄被損壞了。推薦的處理措施有:一方面,調(diào)用CyclicBarrier的reset方法,來喚醒其他由于調(diào)用await方法而被阻塞的線程以避免一直被阻塞,同時將CyclicBarrier實例恢復(fù)至初始化狀態(tài);另一方面,推薦使用具有超時機(jī)制的await方法,以避免線程被永久性阻塞
參考文獻(xiàn)
Java并發(fā)編程之美 翟陸續(xù)、薛賓田著
