java多线程系列:通过对战游戏学习CyclicBarrier
CyclicBarrier是java.util.concurrent包下面的一個工具類,字面意思是可循環使用(Cyclic)的屏障(Barrier),通過它可以實現讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時,所有被屏障攔截的線程才會繼續執行。
這篇文章將介紹CyclicBarrier這個同步工具類的以下幾點
需求
繼上一篇CountDownLatch模擬游戲加載后,現在用戶點擊開始按鈕后,需要匹配包括自己在內的五個玩家才能開始游戲,匹配玩家成功后進入到選擇角色階段。當5位玩家角色都選擇完畢后,開始進入游戲。進入游戲時需要加載相關的數據,待全部玩家都加載完畢后正式開始游戲。
解決方案
從需求中可以知道,想要開始游戲需要經過三個階段,分別是
在這三個階段中,都需要互相等待對方完成才能繼續進入下個階段。?
這時可以采用CyclicBarrier來作為各個階段的節點,等待其他玩家到達,在進入下個階段。
?
?
定義繼承Runnable的類
這里名稱就叫做StartGame,包含兩個屬性
private String player; private CyclicBarrier barrier;通過構造函數初始化兩個屬性
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()+" 進行選擇角色...");choiceRole();System.out.println(this.getPlayer()+" 角色選擇完畢等待其他玩家...");barrier.await();System.out.println(this.getPlayer()+" 開始游戲,進行游戲加載...");loading();System.out.println(this.getPlayer()+" 游戲加載完畢等待其他玩家加載完成...");barrier.await();start();} catch (Exception e){e.printStackTrace();} }其他的方法findOtherPlayer()、choiceRole()等待使用
Thread.sleep()來模擬花費時間
編寫測試代碼
CyclicBarrier有兩個構造函數,如下
public CyclicBarrier(int parties) {} public CyclicBarrier(int parties, Runnable barrierAction) {}先來看看一個參數的構造函數
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(); }測試結果如下
?
?
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("進入下個階段...");} catch (InterruptedException e) {e.printStackTrace();}});再來看看效果
?
?
可以看到在到達某個節點時,會執行實例化CyclicBarrier時傳入的Runnable對象。而且每一次到達都會執行一次。
CyclicBarrier和CountDownLatch的區別
await方法
public int await(){} public int await(long timeout, TimeUnit unit){}無參的await方法這里就不做介紹了,主要介紹下有參的await方法。?
有參的await方法傳入兩個參數,一個是時間、另一個是時間單位?
當調用有參的await方法時會出現下方兩個異常
TimeoutException異常是指調用await方法后等待時間超過傳入的時間,此時會將CyclicBarrier的狀態變成broken,其他調用await方法將會拋出BrokenBarrierException異常,這時的CyclicBarrier將變得不可用,需要調用reset()方法重置CyclicBarrier的狀態。
為什么這么說??
源碼分析一波就可以看出來了?
不管是有參還是無參的await方法都是調用CyclicBarrier的dowait(boolean timed, long nanos)方法,這個方法代碼太長了,截取部分貼出來
在代碼的尾部進行判斷當前等待是否已經超時,如果是會調用breakBarrier()方法,且拋出TimeoutException異常,下面是breakBarrier()的代碼
private void breakBarrier() {generation.broken = true;count = parties;trip.signalAll(); }代碼中將broken狀態置為true,表示當前柵欄移除損壞狀態,且重置柵欄數量,然后喚醒其他等待的線程。此時被喚醒的線程或者其他線程進入dowait方法時,都會拋出BrokenBarrierException異常
案例源代碼地址:https://github.com/rainbowda/learnWay/tree/master/learnConcurrency/src/main/java/com/learnConcurrency/utils/cyclicBarrier
覺得不錯的點個Star,謝謝!
總結
以上是生活随笔為你收集整理的java多线程系列:通过对战游戏学习CyclicBarrier的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 各大公司java面试整理对应问题博客整理
- 下一篇: 【十大经典数据挖掘算法】EM