摘要:當(dāng)位玩家角色都選擇完畢后,開始進(jìn)入游戲。進(jìn)入游戲時(shí)需要加載相關(guān)的數(shù)據(jù),待全部玩家都加載完畢后正式開始游戲。
CyclicBarrier是java.util.concurrent包下面的一個(gè)工具類,字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier),通過它可以實(shí)現(xiàn)讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),所有被屏障攔截的線程才會(huì)繼續(xù)執(zhí)行。
這篇文章將介紹CyclicBarrier這個(gè)同步工具類的以下幾點(diǎn)
通過案例分析
兩種不同構(gòu)造函數(shù)測(cè)試
CyclicBarrier和CountDownLatch的區(qū)別
await方法及源碼分析。
需求繼上一篇CountDownLatch模擬游戲加載后,現(xiàn)在用戶點(diǎn)擊開始按鈕后,需要匹配包括自己在內(nèi)的五個(gè)玩家才能開始游戲,匹配玩家成功后進(jìn)入到選擇角色階段。當(dāng)5位玩家角色都選擇完畢后,開始進(jìn)入游戲。進(jìn)入游戲時(shí)需要加載相關(guān)的數(shù)據(jù),待全部玩家都加載完畢后正式開始游戲。
解決方案從需求中可以知道,想要開始游戲需要經(jīng)過三個(gè)階段,分別是
匹配玩家
選擇角色
加載數(shù)據(jù)
在這三個(gè)階段中,都需要互相等待對(duì)方完成才能繼續(xù)進(jìn)入下個(gè)階段。
這時(shí)可以采用CyclicBarrier來作為各個(gè)階段的節(jié)點(diǎn),等待其他玩家到達(dá),在進(jìn)入下個(gè)階段。
這里名稱就叫做StartGame,包含兩個(gè)屬性
private String player; private CyclicBarrier barrier;
通過構(gòu)造函數(shù)初始化兩個(gè)屬性
public StartGame(String player, CyclicBarrier barrier) { this.player = player; this.barrier = barrier; }
run方法如下
public void run() { try { System.out.println(this.getPlayer()+" 開始匹配玩家..."); findOtherPlayer(); barrier.await(); System.out.println(this.getPlayer()+" 進(jìn)行選擇角色..."); choiceRole(); System.out.println(this.getPlayer()+" 角色選擇完畢等待其他玩家..."); barrier.await(); System.out.println(this.getPlayer()+" 開始游戲,進(jìn)行游戲加載..."); loading(); System.out.println(this.getPlayer()+" 游戲加載完畢等待其他玩家加載完成..."); barrier.await(); start(); } catch (Exception e){ e.printStackTrace(); } }
其他的方法findOtherPlayer()、choiceRole()等待使用
Thread.sleep()
來模擬花費(fèi)時(shí)間
編寫測(cè)試代碼CyclicBarrier有兩個(gè)構(gòu)造函數(shù),如下
public CyclicBarrier(int parties) {} public CyclicBarrier(int parties, Runnable barrierAction) {}
先來看看一個(gè)參數(shù)的構(gòu)造函數(shù)
CyclicBarrier(int parties)public static void main(String[] args) throws IOException { CyclicBarrier barrier = new CyclicBarrier(5); Thread player1 = new Thread(new StartGame("1",barrier)); Thread player2 = new Thread(new StartGame("2",barrier)); Thread player3 = new Thread(new StartGame("3",barrier)); Thread player4 = new Thread(new StartGame("4",barrier)); Thread player5 = new Thread(new StartGame("5",barrier)); player1.start(); player2.start(); player3.start(); player4.start(); player5.start(); System.in.read(); }
測(cè)試結(jié)果如下
CyclicBarrier(int parties, Runnable barrierAction)CyclicBarrier barrier = new CyclicBarrier(5);
替換為
CyclicBarrier barrier = new CyclicBarrier(5, () -> { try { System.out.println("階段完成,等待2秒..."); Thread.sleep(2000); System.out.println("進(jìn)入下個(gè)階段..."); } catch (InterruptedException e) { e.printStackTrace(); } });
再來看看效果
可以看到在到達(dá)某個(gè)節(jié)點(diǎn)時(shí),會(huì)執(zhí)行實(shí)例化CyclicBarrier時(shí)傳入的Runnable對(duì)象。而且每一次到達(dá)都會(huì)執(zhí)行一次。
CyclicBarrier和CountDownLatch的區(qū)別CountDownLatch | CyclicBarrier |
---|---|
計(jì)數(shù)為0時(shí),無法重置 | 計(jì)數(shù)達(dá)到0時(shí),計(jì)數(shù)置為傳入的值重新開始 |
調(diào)用countDown()方法計(jì)數(shù)減一,調(diào)用await()方法只進(jìn)行阻塞,對(duì)計(jì)數(shù)沒任何影響 | 調(diào)用await()方法計(jì)數(shù)減一,若減一后的值不等于0,則線程阻塞 |
不可重復(fù)使用 | 可重復(fù)使用 |
public int await(){} public int await(long timeout, TimeUnit unit){}
無參的await方法這里就不做介紹了,主要介紹下有參的await方法。
有參的await方法傳入兩個(gè)參數(shù),一個(gè)是時(shí)間、另一個(gè)是時(shí)間單位
當(dāng)調(diào)用有參的await方法時(shí)會(huì)出現(xiàn)下方兩個(gè)異常
java.util.concurrent.TimeoutException java.util.concurrent.BrokenBarrierException
TimeoutException異常是指調(diào)用await方法后等待時(shí)間超過傳入的時(shí)間,此時(shí)會(huì)將CyclicBarrier的狀態(tài)變成broken,其他調(diào)用await方法將會(huì)拋出BrokenBarrierException異常,這時(shí)的CyclicBarrier將變得不可用,需要調(diào)用reset()方法重置CyclicBarrier的狀態(tài)。
為什么這么說?
源碼分析一波就可以看出來了
不管是有參還是無參的await方法都是調(diào)用CyclicBarrier的dowait(boolean timed, long nanos)方法,這個(gè)方法代碼太長(zhǎng)了,截取部分貼出來
private int dowait(boolean timed, long nanos){ //加鎖、try catch代碼 final Generation g = generation; //判斷柵欄的狀態(tài) if (g.broken) throw new BrokenBarrierException(); //...省略 int index = --count; //(index == 0) 時(shí)的代碼,省略 for (;;) { try { if (!timed) trip.await(); else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) {} //判斷柵欄的狀態(tài) if (g.broken) throw new BrokenBarrierException(); if (g != generation) return index; //判斷是否是定時(shí)的,且已經(jīng)超時(shí)了 if (timed && nanos <= 0L) { //打破柵欄的狀態(tài) breakBarrier(); throw new TimeoutException(); } } //解鎖 }
在代碼的尾部進(jìn)行判斷當(dāng)前等待是否已經(jīng)超時(shí),如果是會(huì)調(diào)用breakBarrier()方法,且拋出TimeoutException異常,下面是breakBarrier()的代碼
private void breakBarrier() { generation.broken = true; count = parties; trip.signalAll(); }
代碼中將broken狀態(tài)置為true,表示當(dāng)前柵欄移除損壞狀態(tài),且重置柵欄數(shù)量,然后喚醒其他等待的線程。此時(shí)被喚醒的線程或者其他線程進(jìn)入dowait方法時(shí),都會(huì)拋出BrokenBarrierException異常
案例源代碼地址:https://github.com/rainbowda/learnWay/tree/master/learnConcurrency/src/main/java/com/learnConcurrency/utils/cyclicBarrier
覺得不錯(cuò)的點(diǎn)個(gè)Star,謝謝
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77147.html
摘要:前言之前學(xué)多線程的時(shí)候沒有學(xué)習(xí)線程的同步工具類輔助類。而其它線程完成自己的操作后,調(diào)用使計(jì)數(shù)器減。信號(hào)量控制一組線程同時(shí)執(zhí)行。 前言 之前學(xué)多線程的時(shí)候沒有學(xué)習(xí)線程的同步工具類(輔助類)。ps:當(dāng)時(shí)覺得暫時(shí)用不上,認(rèn)為是挺高深的知識(shí)點(diǎn)就沒去管了.. 在前幾天,朋友發(fā)了一篇比較好的Semaphore文章過來,然后在瀏覽博客的時(shí)候又發(fā)現(xiàn)面試還會(huì)考,那還是挺重要的知識(shí)點(diǎn)。于是花了點(diǎn)時(shí)間去了解...
摘要:目前實(shí)現(xiàn)的本地對(duì)戰(zhàn)。盲目樂觀不得不承認(rèn),開發(fā)這個(gè)項(xiàng)目的時(shí)候我顯得過去樂觀了。此處分割線后記開學(xué)新學(xué)期加了軟件體系結(jié)構(gòu),數(shù)值分析,軟件項(xiàng)目管理等課程。此外,編程的幾點(diǎn)注意事項(xiàng)也是沒有達(dá)到。 想了想,光在這里說不給源代碼也是很操蛋的。@影耳 開源在這里 http://git.oschina.net/svtter/DaVinci 希望大家能夠一起完成這個(gè)項(xiàng)目,也希望大家能夠多多指出我的各種不...
摘要:整個(gè)包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見的多線程設(shè)計(jì)模式,設(shè)計(jì)了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對(duì)等進(jìn)行補(bǔ)充增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:在創(chuàng)建對(duì)象時(shí),需要轉(zhuǎn)入一個(gè)值,用于初始化的成員變量,該成員變量表示屏障攔截的線程數(shù)。當(dāng)?shù)竭_(dá)屏障的線程數(shù)小于時(shí),這些線程都會(huì)被阻塞住。當(dāng)所有線程到達(dá)屏障后,將會(huì)被更新,表示進(jìn)入新一輪的運(yùn)行輪次中。 1.簡(jiǎn)介 在分析完AbstractQueuedSynchronizer(以下簡(jiǎn)稱 AQS)和ReentrantLock的原理后,本文將分析 java.util.concurrent 包下的兩個(gè)...
摘要:序包里有幾個(gè)能幫助人們管理相互合作的線程集的類,為多線程常見的應(yīng)用場(chǎng)景預(yù)置了抽象好的類庫(kù)。如果沒報(bào)錯(cuò)就更新屏障狀態(tài)并喚醒所有線程繼續(xù)執(zhí)行。如果還有未到達(dá)的線程,就進(jìn)入一個(gè)死循環(huán),直到超時(shí)線程中斷屏障失效全部完成等情況下退出。 我的博客 轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處。 序 java.util.concurrent包里有幾個(gè)能幫助人們管理相互合作的線程集的類,為多線程常見的應(yīng)用場(chǎng)景預(yù)置了抽象好...
閱讀 3409·2021-09-22 15:01
閱讀 535·2019-08-30 11:11
閱讀 965·2019-08-29 16:17
閱讀 1218·2019-08-29 12:23
閱讀 2036·2019-08-26 11:48
閱讀 3189·2019-08-26 11:48
閱讀 1427·2019-08-26 10:33
閱讀 1938·2019-08-26 10:30