java多線程開發(fā)之通過對(duì)戰(zhàn)游戲?qū)W習(xí)CyclicBarrier

CyclicBarrier是java.util.concurrent包下面的一個(gè)工具類,字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier),通過它可以實(shí)現(xiàn)讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),所有被屏障攔截的線程才會(huì)繼續(xù)執(zhí)行。

創(chuàng)新互聯(lián)建站企業(yè)建站,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),專注于網(wǎng)站建設(shè)技術(shù),精于網(wǎng)頁設(shè)計(jì),有多年建站和網(wǎng)站代運(yùn)營經(jīng)驗(yàn),設(shè)計(jì)師為客戶打造網(wǎng)絡(luò)企業(yè)風(fēng)格,提供周到的建站售前咨詢和貼心的售后服務(wù)。對(duì)于網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)中不同領(lǐng)域進(jìn)行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設(shè)中充分了解客戶行業(yè)的需求,以靈動(dòng)的思維在網(wǎng)頁中充分展現(xiàn),通過對(duì)客戶行業(yè)精準(zhǔn)市場調(diào)研,為客戶提供的解決方案。

這篇文章將介紹CyclicBarrier這個(gè)同步工具類的以下幾點(diǎn)

  1. 通過案例分析
  2. 兩種不同構(gòu)造函數(shù)測試
  3. CyclicBarrier和CountDownLatch的區(qū)別
  4. 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è)階段。

java多線程開發(fā)之通過對(duì)戰(zhàn)游戲?qū)W習(xí)CyclicBarrier

定義繼承Runnable的類

這里名稱就叫做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í)間

編寫測試代碼

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();
}

測試結(jié)果如下

java多線程開發(fā)之通過對(duì)戰(zhàn)游戲?qū)W習(xí)CyclicBarrier

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();
 }

});

再來看看效果

java多線程開發(fā)之通過對(duì)戰(zhàn)游戲?qū)W習(xí)CyclicBarrier

可以看到在到達(dá)某個(gè)節(jié)點(diǎn)時(shí),會(huì)執(zhí)行實(shí)例化CyclicBarrier時(shí)傳入的Runnable對(duì)象。而且每一次到達(dá)都會(huì)執(zhí)行一次。

CyclicBarrier和CountDownLatch的區(qū)別

CountDownLatchCyclicBarrier
計(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ù)使用

await方法

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è)方法代碼太長了,截取部分貼出來

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

當(dāng)前題目:java多線程開發(fā)之通過對(duì)戰(zhàn)游戲?qū)W習(xí)CyclicBarrier
網(wǎng)站URL:http://bm7419.com/article34/jdcese.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營銷型網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站制作、網(wǎng)站排名關(guān)鍵詞優(yōu)化、響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

手機(jī)網(wǎng)站建設(shè)