1. 程式人生 > >【轉】Java多執行緒-CyclicBarrier 柵欄

【轉】Java多執行緒-CyclicBarrier 柵欄

CyclicBarrier 類介紹

CyclicBarrier是一個同步工具類,它允許一組執行緒在到達某個柵欄點(common barrier point)互相等待,發生阻塞,直到最後一個執行緒到達柵欄點,柵欄才會開啟,處於阻塞狀態的執行緒恢復繼續執行.它非常適用於一組執行緒之間必需經常互相等待的情況。CyclicBarrier字面理解是迴圈的柵欄,之所以稱之為迴圈的是因為在等待執行緒釋放後,該柵欄還可以複用。

package com.zhihua.subject;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent
.ExecutorService; import java.util.concurrent.Executors; /** * CyclicBarrier同步工具類 * 指定幾個執行緒執行,只有當執行緒數達到指定數量後, * 執行緒才可以繼續往下執行,不然會一直等待 * @author caizh * */ public class CyclicBarrierTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool
(); final CyclicBarrier cb = new CyclicBarrier(3); for(int i=0;i<3;i++){ Runnable runnable = new Runnable(){ public void run(){ try { Thread.sleep((long)(Math.random()*10000)); System.out.println
("執行緒" + Thread.currentThread().getName() + "即將到達集合地點1,當前已有" + (cb.getNumberWaiting()+1) + "個已經到達," + (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候")); // 等待,除非滿足指定執行緒數 cb.await(); Thread.sleep((long)(Math.random()*10000)); System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點2,當前已有" + (cb.getNumberWaiting()+1) + "個已經到達," + (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候")); // 等待,除非滿足指定執行緒數 cb.await(); Thread.sleep((long)(Math.random()*10000)); System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點3,當前已有" + (cb.getNumberWaiting() + 1) + "個已經到達," + (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候")); cb.await(); } catch (Exception e) { e.printStackTrace(); } } }; service.execute(runnable); } service.shutdown(); } }

建構函式

CyclicBarrier有兩個建構函式:

public CyclicBarrier(int parties) 
public CyclicBarrier(int parties, Runnable barrierAction)

引數parties指定執行緒數量,當指定的執行緒值都到達柵欄點時,柵欄開啟,執行緒恢復。需要注意的是,當指定的執行緒數量大於啟動的執行緒數量,比如修改上例中的程式碼,只啟動9個執行緒,那麼所有的執行緒將一直處於等待狀態。第二種情況是指定的執行緒數量小於啟動的執行緒,上例程式碼,啟動11個執行緒,那麼當第十個執行緒到達柵欄點時,那麼這十個執行緒就會恢復繼續執行,而第十一個執行緒將一直處於阻塞狀態。

CyclicBarrier還提供一個更高階的建構函式CyclicBarrier(int parties, Runnable barrierAction),用於線上程到達屏障時,優先執行barrierAction,方便處理更復雜的業務場景。

public class BarrierDemo2 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(5);
        final CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            public void run() {
                System.out.println("所有執行緒已到達柵欄點");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        for (int i = 0; i < 5; i++) {
            service.execute(new Player("玩家" + i, barrier));
        }
        service.shutdown();
    }
}

輸出結果:

玩家4已準備,等待其他玩家準備...
玩家2已準備,等待其他玩家準備...
玩家1已準備,等待其他玩家準備...
玩家0已準備,等待其他玩家準備...
玩家3已準備,等待其他玩家準備...
所有執行緒已到達柵欄點
玩家0已加入遊戲
玩家1已加入遊戲
玩家3已加入遊戲
玩家4已加入遊戲
玩家2已加入遊戲

常用方法介紹

  • await():呼叫該方法會使當前執行緒在柵欄點發生阻塞,直到指定的執行緒數量都達到柵欄點時恢復執行
  • await(long timeout, TimeUnit unit):類似於await(),增加了超時時間引數。
    當barrier在等待點等待超時時,會丟擲TimeoutException異常,同時,位於該barrier上的其他執行緒也將毀丟擲
    BrokenBarrierException異常。這裡說明,barrier上的執行緒要麼同時成功要麼同時失敗,不存在部分成功部分失敗的場景。
  • getNumberWaiting():返回當前在柵欄處等待的參與者數目。此方法主要用於除錯和斷言。
  • getParties():該方法可以獲得建構函式中指定的需要在柵欄點阻塞的執行緒數量。
  • isBroken():查詢此柵欄是否處於損壞狀態。
  • reset():將barrier重置為其初始狀態。如果所有參與者目前都在屏障處等待,則它們將返回,同時丟擲一個
    BrokenBarrierException