Java并發(fā)實(shí)例之CyclicBarrier的使用
最近一直整并發(fā)這塊東西,順便寫(xiě)點(diǎn)Java并發(fā)的例子,給大家做個(gè)分享,也強(qiáng)化下自己記憶,如果有什么錯(cuò)誤或者不當(dāng)?shù)牡胤剑瑲g迎大家斧正。
CyclicBarrier是一種多線(xiàn)程并發(fā)控制實(shí)用工具,和CountDownLatch非常類(lèi)似,它也可以實(shí)現(xiàn)線(xiàn)程間的計(jì)數(shù)等待,但是它的功能比CountDownLatch更加復(fù)雜且強(qiáng)大。
CyclicBarrier的介紹
CyclicBarrier 的字面意思是可循環(huán)(Cyclic)使用的屏障(Barrier)。它要做的事情是,讓一組線(xiàn)程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線(xiàn)程到達(dá)屏障時(shí),屏障才會(huì)開(kāi)門(mén),所有被屏障攔截的線(xiàn)程才會(huì)繼續(xù)干活。線(xiàn)程進(jìn)入屏障通過(guò)CyclicBarrier的await()方法。
CyclicBarrier默認(rèn)的構(gòu)造方法是CyclicBarrier(int parties),其參數(shù)表示屏障攔截的線(xiàn)程數(shù)量,每個(gè)線(xiàn)程調(diào)用await方法告訴CyclicBarrier我已經(jīng)到達(dá)了屏障,然后當(dāng)前線(xiàn)程被阻塞。
CyclicBarrier還提供一個(gè)更高級(jí)的構(gòu)造函數(shù)CyclicBarrier(int parties, Runnable barrierAction),用于在線(xiàn)程到達(dá)屏障時(shí),優(yōu)先執(zhí)行barrierAction這個(gè)Runnable對(duì)象,方便處理更復(fù)雜的業(yè)務(wù)場(chǎng)景。
public CyclicBarrier(int parties) { this(parties, null); } public int getParties() { return parties; }
實(shí)現(xiàn)原理:在CyclicBarrier的內(nèi)部定義了一個(gè)Lock對(duì)象,每當(dāng)一個(gè)線(xiàn)程調(diào)用CyclicBarrier的await方法時(shí),將剩余攔截的線(xiàn)程數(shù)減1,然后判斷剩余攔截?cái)?shù)是否為0,如果不是,進(jìn)入Lock對(duì)象的條件隊(duì)列等待。如果是,執(zhí)行barrierAction對(duì)象的Runnable方法,然后將鎖的條件隊(duì)列中的所有線(xiàn)程放入鎖等待隊(duì)列中,這些線(xiàn)程會(huì)依次的獲取鎖、釋放鎖,接著先從await方法返回,再?gòu)腃yclicBarrier的await方法中返回。
CyclicBarrier主要用于一組線(xiàn)程之間的相互等待,而CountDownLatch一般用于一組線(xiàn)程等待另一組些線(xiàn)程。實(shí)際上可以通過(guò)CountDownLatch的countDown()和await()來(lái)實(shí)現(xiàn)CyclicBarrier的功能。即 CountDownLatch中的countDown()+await() = CyclicBarrier中的await()。注意:在一個(gè)線(xiàn)程中先調(diào)用countDown(),然后調(diào)用await()。
構(gòu)造函數(shù)CyclicBarrier可以理解為循環(huán)柵欄,這個(gè)計(jì)數(shù)器可以反復(fù)使用。比如,假設(shè)我們將計(jì)數(shù)器設(shè)置為10,那么湊齊第一批10個(gè)線(xiàn)程后,計(jì)數(shù)器就會(huì)歸零,然后接著湊齊下一批10個(gè)線(xiàn)程,這就是它的內(nèi)在含義。
LOL和王者榮耀的玩家很多,許多人應(yīng)該都有打大龍的經(jīng)歷,話(huà)說(shuō)前期大家打算一起去偷大龍,由于前期大家都比較弱,需要五個(gè)人都齊了才能打大龍,這樣程序該如何實(shí)現(xiàn)呢?本人很菜,開(kāi)始我的代碼是這么寫(xiě)的(哈哈大家不要糾結(jié)我的時(shí)間):
public class KillDragon { /** * 模擬打野去打大龍 */ public static void dayePlayDragon(){ System.out.println("打野在去打大龍的路上,需要10s"); } /** * 模擬上單去打大龍 */ public static void shangdanPlayDragon(){ System.out.println("上單在去打大龍的路上,需要10s"); } /** * 模擬中單去打大龍 */ public static void zhongdanPlayDragon(){ System.out.println("中單在去打大龍的路上,需要10s"); } /** * 模擬ADC和輔助去打大龍 */ public static void adcAndFuzhuPlayDragon(){ System.out.println("ADC和輔助在去打大龍的路上,需要10s"); } /** * 模擬大家一起去打大龍 */ public static void killDragon() { System.out.println("打大龍..."); } public static void main(String[] args) { dayePlayDragon(); shangdanPlayDragon(); zhongdanPlayDragon(); adcAndFuzhuPlayDragon(); killDragon(); }
結(jié)果如下:
打野在去打大龍的路上,需要10s 上單在去打大龍的路上,需要10s 中單在去打大龍的路上,需要10s ADC和輔助在去打大龍的路上,需要10s 打大龍...
這完了,大家在路上的時(shí)間就花了40s了,顯然是錯(cuò)誤的。要是都這么干,對(duì)方把你塔都要偷光了。不行得改進(jìn)下,怎么改呢,多線(xiàn)程并發(fā)執(zhí)行,如是我改成了下面這樣的,用volatile關(guān)鍵字。
private static volatile int i = 4; public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); while (i!=0){ } while (i==0) { killDragon(); i--; long t = System.currentTimeMillis() - start; System.out.println("總共耗時(shí):"+t+"毫秒"); } } }).start(); new Thread(new Runnable() { @Override public void run() { dayePlayDragon(); try { Thread.sleep(10000); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { shangdanPlayDragon(); try { Thread.sleep(10000); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { zhongdanPlayDragon(); try { Thread.sleep(10000); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { adcAndFuzhuPlayDragon(); try { Thread.sleep(10000); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }
結(jié)果如下:
打野在去打大龍的路上,需要10s 上單在去打大龍的路上,需要10s 中單在去打大龍的路上,需要10s ADC和輔助在去打大龍的路上,需要10s 打大龍... 總共耗時(shí):10005毫秒
結(jié)果似乎還不錯(cuò),但是處理起來(lái)實(shí)在是有點(diǎn)麻煩,需要 while (i!=0)一直在那循環(huán)著。這時(shí)候?qū)W到了用 CyclicBarrier來(lái)處理,代碼如下:
public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(5); new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } killDragon(); long t = System.currentTimeMillis() - start; System.out.println("總共耗時(shí):"+t+"毫秒"); } }).start(); new Thread(new Runnable() { @Override public void run() { dayePlayDragon(); try { Thread.sleep(10000); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { shangdanPlayDragon(); try { Thread.sleep(10000); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { zhongdanPlayDragon(); try { Thread.sleep(10000); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { adcAndFuzhuPlayDragon(); try { Thread.sleep(10000); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); }
大家都沒(méi)到達(dá)之前都等待,結(jié)果如下:
打野在去打大龍的路上,需要10s 上單在去打大龍的路上,需要10s 中單在去打大龍的路上,需要10s ADC和輔助在去打大龍的路上,需要10s 打大龍... 總共耗時(shí):10002毫秒
CyclicBarrier相當(dāng)于線(xiàn)程的計(jì)數(shù)器:
CyclicBarrier初始化時(shí)規(guī)定一個(gè)數(shù)目,然后計(jì)算調(diào)用了CyclicBarrier.await()進(jìn)入等待的線(xiàn)程數(shù)。當(dāng)線(xiàn)程數(shù)達(dá)到了這個(gè)數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線(xiàn)程被喚醒并繼續(xù)。
CyclicBarrier就象它名字的意思一樣,可看成是個(gè)障礙, 所有的線(xiàn)程必須到齊后才能一起通過(guò)這個(gè)障礙。
CyclicBarrier初始時(shí)還可帶一個(gè)Runnable的參數(shù), 此Runnable任務(wù)在CyclicBarrier的數(shù)目達(dá)到后,所有其它線(xiàn)程被喚醒前被執(zhí)行。
當(dāng)然這樣使用CyclicBarrier和使用CountDownLatch是沒(méi)什么區(qū)別的,正如前文所說(shuō)的CyclicBarrier的功能更加的復(fù)雜且強(qiáng)大。給大家看一個(gè)《實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)》一書(shū)上的一個(gè)例子。
比如:司令下達(dá)命令,要求10個(gè)士兵去一起完成一項(xiàng)任務(wù)。這時(shí),就會(huì)要求10個(gè)士兵先集合報(bào)道,接著,一起雄赳赳氣昂昂地去執(zhí)行任務(wù)。當(dāng)10個(gè)士兵都執(zhí)行完了任務(wù),那么司機(jī)就可以對(duì)外宣稱(chēng),任務(wù)完成。相比CountDownLatch,CyclicBarrier可以接受一個(gè)參數(shù)作為BarrierAction。所謂的BarrierAction就是當(dāng)計(jì)數(shù)器一次計(jì)數(shù)完成后,系統(tǒng)會(huì)執(zhí)行的動(dòng)作。如下構(gòu)造函數(shù),其中,parties表示技術(shù)總數(shù),也就是參與的線(xiàn)程總數(shù)。
public CyclicBarrier(int parties, Runnable barrierAction)
下面示例演示了上述任務(wù)場(chǎng)景
public class CyclicBarrierDemo { public static class Soldier implements Runnable { private String soldier; private final CyclicBarrier cyclicBarrier; public Soldier(CyclicBarrier cyclicBarrier, String soldier) { this.soldier = soldier; this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { cyclicBarrier.await(); doWork(); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } void doWork() { try { Thread.sleep(Math.abs(new Random().nextInt() % 10000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(soldier + ":任務(wù)完成"); } } public static class BarrierRun implements Runnable { boolean flag; int N; public BarrierRun(boolean flag, int N) { this.flag = flag; this.N = N; } @Override public void run() { if (flag) { System.out.println("司令:[士兵" + N + "個(gè),任務(wù)完成!"); } else { System.out.println("司令:[士兵" + N + "個(gè),集合完畢!"); flag = true; } } } public static void main(String args[]) { final int N = 10; Thread[] allSoldier = new Thread[N]; boolean flag = false; CyclicBarrier cyclicBarrier = new CyclicBarrier(N, new BarrierRun(flag, N)); System.out.println("集合隊(duì)伍!"); for (int i = 0; i < N; i++) { System.out.println("士兵" + i + "報(bào)道!"); allSoldier[i] = new Thread(new Soldier(cyclicBarrier, "士兵" + i)); allSoldier[i].start(); } } }
執(zhí)行結(jié)果如下:
集合隊(duì)伍! 士兵0報(bào)道! 士兵1報(bào)道! 士兵2報(bào)道! 士兵3報(bào)道! 士兵4報(bào)道! 士兵5報(bào)道! 士兵6報(bào)道! 士兵7報(bào)道! 士兵8報(bào)道! 士兵9報(bào)道! 司令:[士兵10個(gè),集合完畢! 士兵0:任務(wù)完成 士兵2:任務(wù)完成 士兵9:任務(wù)完成 士兵3:任務(wù)完成 士兵7:任務(wù)完成 士兵8:任務(wù)完成 士兵1:任務(wù)完成 士兵4:任務(wù)完成 士兵5:任務(wù)完成 士兵6:任務(wù)完成 司令:[士兵10個(gè),任務(wù)完成!
總結(jié)
以上就是本文關(guān)于Java并發(fā)實(shí)例之CyclicBarrier的使用的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
Javaweb應(yīng)用使用限流處理大量的并發(fā)請(qǐng)求詳解
java并發(fā)學(xué)習(xí)之BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者詳解
如有不足之處,歡迎留言指出。期待您的寶貴意見(jiàn)!
相關(guān)文章
Mybatis-Plus同時(shí)使用邏輯刪除和唯一索引的問(wèn)題及解決辦法(報(bào)數(shù)據(jù)重復(fù)Duplicate entry的
在開(kāi)發(fā)中,我們經(jīng)常會(huì)有邏輯刪除和唯一索引同時(shí)使用的情況,但當(dāng)使用mybatis plus時(shí),如果同時(shí)使用邏輯刪除和唯一索引,會(huì)報(bào)數(shù)據(jù)重復(fù)Duplicate entry的問(wèn)題,如何解決這個(gè)問(wèn)題呢,小編給大家分享Mybatis-Plus同時(shí)使用邏輯刪除和唯一索引的問(wèn)題及解決辦法,一起看看吧2023-11-11簡(jiǎn)單易用的Spring?Boot郵件發(fā)送demo
本文將介紹如何使用Spring?Boot發(fā)送郵件,我們將演示如何配置SMTP郵件服務(wù)器,創(chuàng)建一個(gè)郵件模板,以及如何使用JavaMailSender發(fā)送郵件,我們還將介紹如何測(cè)試我們的郵件發(fā)送代碼2023-12-12Java實(shí)現(xiàn)拓?fù)渑判蛩惴ǖ氖纠a
在圖論中,拓?fù)渑判颍═opological Sorting)是一個(gè)有向無(wú)環(huán)圖(DAG, Directed Acyclic Graph)的所有頂點(diǎn)的線(xiàn)性序列。本文將為大家講講拓?fù)渑判蛩惴ǖ脑砑皩?shí)現(xiàn),需要的可以參考一下2022-07-07java外部類(lèi)與內(nèi)部類(lèi)的關(guān)系詳解
本文詳細(xì)講解了java外部類(lèi)與內(nèi)部類(lèi)的關(guān)系,用代碼演示了外部類(lèi)調(diào)用內(nèi)部類(lèi)的方法。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12SpringBoot監(jiān)控模塊Actuator的用法詳解
Spring?Boot?Actuator?是?Spring?Boot?自帶的一個(gè)功能模塊,提供了一組已經(jīng)開(kāi)箱即用的生產(chǎn)環(huán)境下常用的特性和服務(wù),比如應(yīng)用程序的健康檢查、信息暴露、度量收集、日志記錄等,本文將給大家詳細(xì)SpringBoot監(jiān)控模塊Actuator的用法2023-06-06Java通過(guò)XPath獲取XML文件中符合特定條件的節(jié)點(diǎn)
今天小編就為大家分享一篇關(guān)于Java通過(guò)XPath獲取XML文件中符合特定條件的節(jié)點(diǎn),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Java使用線(xiàn)程池批量處理數(shù)據(jù)操作具體流程
這篇文章主要給大家介紹了關(guān)于Java使用線(xiàn)程池批量處理數(shù)據(jù)操作的相關(guān)資料,Java多線(xiàn)程編程中線(xiàn)程池是一個(gè)非常重要的概念,線(xiàn)程池可以提高線(xiàn)程的復(fù)用率和任務(wù)調(diào)度的效率,尤其是當(dāng)需要查詢(xún)大批量數(shù)據(jù)時(shí),需要的朋友可以參考下2023-06-06Java實(shí)現(xiàn)人臉識(shí)別登錄、注冊(cè)等功能(最新完整版)
這段時(shí)間由于學(xué)校實(shí)行靜態(tài)化管理,寢室門(mén)和校門(mén)都是用了人臉識(shí)別的裝置,本系列項(xiàng)目從設(shè)計(jì)到實(shí)現(xiàn)源碼全部開(kāi)源免費(fèi)學(xué)習(xí)使用,對(duì)Java實(shí)現(xiàn)人臉識(shí)別登錄、注冊(cè)功能感興趣的朋友一起看看吧2022-05-05