Java多線程10 同步工具類CyclicBarrier

Java多線程目錄
CyclicBarrier是一個(gè)同步工具類捕儒,它允許一組線程互相等待,直到達(dá)到某個(gè)公共屏障點(diǎn)伴奥。與CountDownLatch不同的是該barrier在釋放線程等待后可以重用,所以它稱為循環(huán)(Cyclic)的屏障(Barrier)。
CyclicBarrier支持一個(gè)可選的Runnable命令默蚌,在一組線程中的最后一個(gè)線程到達(dá)之后(但在釋放所有線程之前),該命令只在每個(gè)屏障點(diǎn)運(yùn)行一次苇羡。若再繼續(xù)所有的參與線程之前更新共享狀態(tài)绸吸,此屏蔽操作很有用。

1 CyclicBarrier方法說明

CyclicBarrier提供的方法有:

CyclicBarrier(parties):初始化相互等待的線程數(shù)量的構(gòu)造方法宣虾。

CyclicBarrier(parties,Runnable barrierAction):初始化相互等待的線程數(shù)量以及屏障線程的構(gòu)造方法惯裕。
屏障線程的運(yùn)行時(shí)機(jī):
等待的線程數(shù)量=parties之后,CyclicBarrier打開屏障之前绣硝。
舉例:在分組計(jì)算中蜻势,每個(gè)線程負(fù)責(zé)一部分計(jì)算,最終這些線程計(jì)算結(jié)束之后鹉胖,交由屏障線程進(jìn)行匯總計(jì)算握玛。

int getParties():獲取CyclicBarrier打開屏障的線程數(shù)量够傍,也成為方數(shù)。

int getNumberWaiting():獲取正在CyclicBarrier上等待的線程數(shù)量挠铲。

int await():在CyclicBarrier上進(jìn)行阻塞等待冕屯,直到發(fā)生以下情形之一:

  • 在CyclicBarrier上等待的線程數(shù)量達(dá)到parties,則所有線程被釋放拂苹,繼續(xù)執(zhí)行安聘。
  • 當(dāng)前線程被中斷,則拋出InterruptedException異常瓢棒,并停止等待浴韭,繼續(xù)執(zhí)行。
  • 其他等待的線程被中斷脯宿,則當(dāng)前線程拋出BrokenBarrierException異常念颈,并停止等待,繼續(xù)執(zhí)行连霉。
  • 其他等待的線程超時(shí)榴芳,則當(dāng)前線程拋出BrokenBarrierException異常,并停止等待跺撼,繼續(xù)執(zhí)行窟感。
  • 其他線程調(diào)用CyclicBarrier.reset()方法,則當(dāng)前線程拋出BrokenBarrierException異常财边,并停止等待肌括,繼續(xù)執(zhí)行。

int await(timeout,TimeUnit):在CyclicBarrier上進(jìn)行限時(shí)的阻塞等待酣难,直到發(fā)生以下情形之一:

  • 在CyclicBarrier上等待的線程數(shù)量達(dá)到parties谍夭,則所有線程被釋放,繼續(xù)執(zhí)行憨募。
  • 當(dāng)前線程被中斷紧索,則拋出InterruptedException異常,并停止等待菜谣,繼續(xù)執(zhí)行珠漂。
  • 當(dāng)前線程等待超時(shí),則拋出TimeoutException異常尾膊,并停止等待媳危,繼續(xù)執(zhí)行。
  • 其他等待的線程被中斷冈敛,則當(dāng)前線程拋出BrokenBarrierException異常待笑,并停止等待,繼續(xù)執(zhí)行抓谴。
  • 其他等待的線程超時(shí)暮蹂,則當(dāng)前線程拋出BrokenBarrierException異常寞缝,并停止等待,繼續(xù)執(zhí)行仰泻。
  • 其他線程調(diào)用CyclicBarrier.reset()方法荆陆,則當(dāng)前線程拋出BrokenBarrierException異常,并停止等待集侯,繼續(xù)執(zhí)行被啼。

boolean isBroken():獲取是否破損標(biāo)志位broken的值,此值有以下幾種情況:

  • CyclicBarrier初始化時(shí)浅悉,broken=false趟据,表示屏障未破損券犁。
  • 如果正在等待的線程被中斷术健,則broken=true,表示屏障破損粘衬。
  • 如果正在等待的線程超時(shí)荞估,則broken=true,表示屏障破損稚新。
  • 如果有線程調(diào)用CyclicBarrier.reset()方法勘伺,則broken=false,表示屏障回到未破損狀態(tài)褂删。

void reset():使得CyclicBarrier回歸初始狀態(tài)飞醉,直觀來看它做了兩件事:

  • 如果有正在等待的線程,則會(huì)拋出BrokenBarrierException異常屯阀,且這些線程停止等待缅帘,繼續(xù)執(zhí)行。
  • 將是否破損標(biāo)志位broken置為false难衰。

2 CyclicBarrier實(shí)例

假若有若干個(gè)線程都要進(jìn)行寫數(shù)據(jù)操作钦无,并且只有所有線程都完成寫數(shù)據(jù)操作之后,這些線程才能繼續(xù)做后面的事情盖袭,此時(shí)就可以利用CyclicBarrier了:

 public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
            try {
                Thread.sleep(5000);      //以睡眠來模擬寫入數(shù)據(jù)操作
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢失暂,等待其他線程寫入完畢");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有線程寫入完畢,繼續(xù)處理其他任務(wù)...");
        }
    }
線程Thread-0正在寫入數(shù)據(jù)...
線程Thread-3正在寫入數(shù)據(jù)...
線程Thread-1正在寫入數(shù)據(jù)...
線程Thread-2正在寫入數(shù)據(jù)...
線程Thread-1寫入數(shù)據(jù)完畢鳄虱,等待其他線程寫入完畢
線程Thread-3寫入數(shù)據(jù)完畢弟塞,等待其他線程寫入完畢
線程Thread-2寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-0寫入數(shù)據(jù)完畢拙已,等待其他線程寫入完畢
所有線程寫入完畢决记,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢悠栓,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢霉涨,繼續(xù)處理其他任務(wù)...

從上面輸出結(jié)果可以看出按价,每個(gè)寫入線程執(zhí)行完寫數(shù)據(jù)操作之后,就在等待其他線程寫入操作完畢笙瑟。

當(dāng)所有線程線程寫入操作完畢之后楼镐,所有線程就繼續(xù)進(jìn)行后續(xù)的操作了。

如果想在所有線程寫入操作完之后往枷,進(jìn)行額外的其他操作可以為CyclicBarrier提供Runnable參數(shù):
public class CyclicBarrierTest {

    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("當(dāng)前線程"+Thread.currentThread().getName());
            }
        });

        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
            try {
                Thread.sleep(3000);      //以睡眠來模擬寫入數(shù)據(jù)操作
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢框产,等待其他線程寫入完畢");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有線程寫入完畢,繼續(xù)處理其他任務(wù)...");
        }
    }

}
線程Thread-0正在寫入數(shù)據(jù)...
線程Thread-3正在寫入數(shù)據(jù)...
線程Thread-2正在寫入數(shù)據(jù)...
線程Thread-1正在寫入數(shù)據(jù)...
線程Thread-1寫入數(shù)據(jù)完畢错洁,等待其他線程寫入完畢
線程Thread-3寫入數(shù)據(jù)完畢秉宿,等待其他線程寫入完畢
線程Thread-0寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-2寫入數(shù)據(jù)完畢屯碴,等待其他線程寫入完畢
當(dāng)前線程Thread-2
所有線程寫入完畢描睦,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢导而,繼續(xù)處理其他任務(wù)...
所有線程寫入完畢忱叭,繼續(xù)處理其他任務(wù)...

從結(jié)果可以看出,當(dāng)四個(gè)線程都到達(dá)barrier狀態(tài)后今艺,會(huì)從四個(gè)線程中選擇一個(gè)線程去執(zhí)行Runnable韵丑。

await指定時(shí)間的效果:
public class CyclicBarrierTest {


    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier = new CyclicBarrier(N);

        for (int i = 0; i < N; i++) {
            if (i < N - 1)
                new Writer(barrier).start();
            else {
                try {
                    //運(yùn)行時(shí)間遠(yuǎn)小于2000(cyclicBarrier.await 指定時(shí)間) 就不會(huì)拋出TimeoutException
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                new Writer(barrier).start();
            }
            
        }
    }

    static class Writer extends Thread {
        private CyclicBarrier cyclicBarrier;

        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("線程" + Thread.currentThread().getName() + "正在寫入數(shù)據(jù)...");
            try {
                Thread.sleep(3000);      //以睡眠來模擬寫入數(shù)據(jù)操作
                System.out.println("線程" + Thread.currentThread().getName() + "寫入數(shù)據(jù)完畢,等待其他線程寫入完畢");
                try {
                    cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "所有線程寫入完畢虚缎,繼續(xù)處理其他任務(wù)...");
        }
    }
}
線程Thread-0正在寫入數(shù)據(jù)...
線程Thread-2正在寫入數(shù)據(jù)...
線程Thread-1正在寫入數(shù)據(jù)...
線程Thread-0寫入數(shù)據(jù)完畢撵彻,等待其他線程寫入完畢
線程Thread-2寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-1寫入數(shù)據(jù)完畢实牡,等待其他線程寫入完畢
線程Thread-3正在寫入數(shù)據(jù)...
java.util.concurrent.TimeoutException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
    at CyclicBarrierTest$Writer.run(CyclicBarrierTest.java:43)
Thread-0所有線程寫入完畢陌僵,繼續(xù)處理其他任務(wù)...
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
    at CyclicBarrierTest$Writer.run(CyclicBarrierTest.java:43)
Thread-1所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
    at CyclicBarrierTest$Writer.run(CyclicBarrierTest.java:43)
Thread-2所有線程寫入完畢铲掐,繼續(xù)處理其他任務(wù)...
線程Thread-3寫入數(shù)據(jù)完畢拾弃,等待其他線程寫入完畢
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
    at CyclicBarrierTest$Writer.run(CyclicBarrierTest.java:43)
Thread-3所有線程寫入完畢,繼續(xù)處理其他任務(wù)...

上面的代碼在main方法的for循環(huán)中摆霉,故意讓最后一個(gè)線程啟動(dòng)延遲豪椿,因?yàn)樵谇懊嫒齻€(gè)線程都達(dá)到barrier之后,等待了指定的時(shí)間發(fā)現(xiàn)第四個(gè)線程還沒有達(dá)到barrier携栋,就拋出異常并繼續(xù)執(zhí)行后面的任務(wù)搭盾。

另外CyclicBarrier是可以重用的,看下面這個(gè)例子:
public class CyclicBarrierTest {

    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);

        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("CyclicBarrier重用");

        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
            try {
                Thread.sleep(3000);      //以睡眠來模擬寫入數(shù)據(jù)操作
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢婉支,等待其他線程寫入完畢");

                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"所有線程寫入完畢鸯隅,繼續(xù)處理其他任務(wù)...");
        }
    }
}
線程Thread-0正在寫入數(shù)據(jù)...
線程Thread-3正在寫入數(shù)據(jù)...
線程Thread-2正在寫入數(shù)據(jù)...
線程Thread-1正在寫入數(shù)據(jù)...
線程Thread-1寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-0寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-3寫入數(shù)據(jù)完畢蝌以,等待其他線程寫入完畢
線程Thread-2寫入數(shù)據(jù)完畢炕舵,等待其他線程寫入完畢
Thread-2所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
Thread-1所有線程寫入完畢跟畅,繼續(xù)處理其他任務(wù)...
Thread-3所有線程寫入完畢咽筋,繼續(xù)處理其他任務(wù)...
Thread-0所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
CyclicBarrier重用
線程Thread-4正在寫入數(shù)據(jù)...
線程Thread-5正在寫入數(shù)據(jù)...
線程Thread-6正在寫入數(shù)據(jù)...
線程Thread-7正在寫入數(shù)據(jù)...
線程Thread-5寫入數(shù)據(jù)完畢徊件,等待其他線程寫入完畢
線程Thread-4寫入數(shù)據(jù)完畢奸攻,等待其他線程寫入完畢
線程Thread-7寫入數(shù)據(jù)完畢,等待其他線程寫入完畢
線程Thread-6寫入數(shù)據(jù)完畢虱痕,等待其他線程寫入完畢
Thread-6所有線程寫入完畢睹耐,繼續(xù)處理其他任務(wù)...
Thread-5所有線程寫入完畢,繼續(xù)處理其他任務(wù)...
Thread-4所有線程寫入完畢部翘,繼續(xù)處理其他任務(wù)...
Thread-7所有線程寫入完畢硝训,繼續(xù)處理其他任務(wù)...

從執(zhí)行結(jié)果可以看出,在初次的4個(gè)線程越過barrier狀態(tài)后略就,又可以用來進(jìn)行新一輪的使用捎迫。而CountDownLatch無法進(jìn)行重復(fù)使用。

3 CyclicBarrier源碼解析
先看一下CyclicBarrier中成員變量的組成:

    /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    /** The number of parties */
    private final int parties;//攔截的線程數(shù)量
    /* The command to run when tripped */
    private final Runnable barrierCommand; //當(dāng)屏障撤銷時(shí)表牢,需要執(zhí)行的屏障操作
    //當(dāng)前的Generation。每當(dāng)屏障失效或者開閘之后都會(huì)自動(dòng)替換掉贝次。從而實(shí)現(xiàn)重置的功能崔兴。
    private Generation generation = new Generation();

    /**
     * Number of parties still waiting. Counts down from parties to 0
     * on each generation.  It is reset to parties on each new
     * generation or when broken.
     */
    private int count;

可以看出,CyclicBarrier是由ReentrantLock和Condition來實(shí)現(xiàn)的蛔翅。具體每個(gè)變量都有什么意義敲茄,我們?cè)诜治鲈创a的時(shí)候具體說。
我們主要從CyclicBarrier的構(gòu)造方法和它的await方法分析說起山析。

CyclicBarrier構(gòu)造函數(shù)

CyclicBarrier有兩個(gè)構(gòu)造函數(shù):

//帶Runnable參數(shù)的函數(shù)
 public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;//有幾個(gè)運(yùn)動(dòng)員要參賽
        this.count = parties;//目前還需要幾個(gè)運(yùn)動(dòng)員準(zhǔn)備好
        //你要在所有線程都繼續(xù)執(zhí)行下去之前要執(zhí)行什么操作堰燎,可以為空
        this.barrierCommand = barrierAction;
    }
//不帶Runnable參數(shù)的函數(shù)
 public CyclicBarrier(int parties) {
     this(parties, null);
 }

其中,第二個(gè)構(gòu)造函數(shù)調(diào)用的是第一個(gè)構(gòu)造函數(shù)笋轨,這個(gè) Runnable barrierAction 參數(shù)是什么呢秆剪?其實(shí)在上面的小示例中我們就用到了這個(gè)Runnable參數(shù),它就是在所有線程都準(zhǔn)備好之后爵政,滿足Barrier條件時(shí)仅讽,并且在所有線程繼續(xù)執(zhí)行之前,我們可以執(zhí)行這個(gè)Runnable钾挟。但是值得注意的是洁灵,這不是新起了一個(gè)線程,而是通過最后一個(gè)準(zhǔn)備好的(也就是最后一個(gè)到達(dá)Barrier的)線程承擔(dān)啟動(dòng)的掺出。這一點(diǎn)我們?cè)谏厦媸纠写蛴〉倪\(yùn)行結(jié)果中也可以看出來:Thread-2線程是最后一個(gè)準(zhǔn)備好的徽千,就是它執(zhí)行的這個(gè)barrierAction苫费。
這里parties和count不要混淆,parties是表示必須有幾個(gè)線程要到達(dá)Barrier双抽,而count是表示目前還有幾個(gè)線程未到達(dá)Barrier黍衙。也就是說,只有當(dāng)count參數(shù)為0時(shí)荠诬,Barrier條件即滿足琅翻,所有線程可以繼續(xù)執(zhí)行。
count變量是怎么減少到0的呢柑贞?是通過Barrier執(zhí)行的await方法方椎。下面我們就看一下await方法。

await方法
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
await方法調(diào)用的dowait方法:
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();//獲取ReentrantLock互斥鎖
        try {
            final Generation g = generation;//獲取generation對(duì)象

            if (g.broken)//如果generation損壞钧嘶,拋出異常
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                //如果當(dāng)前線程被中斷棠众,則調(diào)用breakBarrier方法,停止CyclicBarrier有决,并喚醒所有線程
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;// 看到這里了吧闸拿,count減1 
            //index=0,也就是說书幕,有0個(gè)線程未滿足CyclicBarrier條件新荤,也就是條件滿足,
            //可以喚醒所有的線程了
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                   //這就是構(gòu)造器的第二個(gè)參數(shù)台汇,如果不為空的話苛骨,就執(zhí)行這個(gè)Runnable的run方法,
                   //你看苟呐,這里是執(zhí)行的是run方法痒芝,也就是說,并沒有新起一個(gè)另外的線程牵素,
                   //而是最后一個(gè)執(zhí)行await操作的線程執(zhí)行的這個(gè)run方法严衬。
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run(); //同步執(zhí)行barrierCommand
                    ranAction = true;
                    nextGeneration(); //執(zhí)行成功設(shè)置下一個(gè)nextGeneration
                    return 0;
                } finally {
                    if (!ranAction) . //如果barrierCommand執(zhí)行失敗,進(jìn)行屏障破壞處理
                        breakBarrier();
                }
            }
            //如果當(dāng)前線程不是最后一個(gè)到達(dá)的線程
            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await(); //調(diào)用Condition的await()方法阻塞
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos); //調(diào)用Condition的awaitNanos()方法阻塞
                } catch (InterruptedException ie) {
                //如果當(dāng)前線程被中斷笆呆,則判斷是否有其他線程已經(jīng)使屏障破壞请琳。若沒有則進(jìn)行屏障破壞處理,并拋出異常腰奋;否則再次中斷當(dāng)前線程
                    if (g == generation && ! g.broken) {
                        breakBarrier();//執(zhí)行breakBarrier单起,喚醒所有線程
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)//如果當(dāng)前generation已經(jīng)損壞,拋出異常
                    throw new BrokenBarrierException();

                if (g != generation)//如果generation已經(jīng)更新?lián)Q代劣坊,則返回index
                    return index;
                //如果是參數(shù)是超時(shí)等待嘀倒,并且已經(jīng)超時(shí),則執(zhí)行breakBarrier()方法
                //喚醒所有等待線程。
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

簡(jiǎn)單來說测蘑,如果不發(fā)生異常灌危,線程不被中斷,那么dowait方法會(huì)調(diào)用Condition的await方法(具體Condition的原理請(qǐng)看前面的文章)碳胳,直到所有線程都準(zhǔn)備好勇蝙,即都執(zhí)行了dowait方法,(做count的減操作挨约,直到count=0)味混,即CyclicBarrier條件已滿足,就會(huì)執(zhí)行喚醒線程操作诫惭,也就是上面的nextGeneration()方法翁锡。可能大家會(huì)有疑惑夕土,這個(gè)Generation是什么東西呢馆衔?其實(shí)這個(gè)Generation定義的很簡(jiǎn)單,就一個(gè)布爾值的成員變量:

private Generation generation = new Generation();

private static class Generation {
    boolean broken = false;
}

Generation 可以理解成“代”怨绣,我們要知道角溃,CyclicBarrier是可以重復(fù)使用的,CyclicBarrier中的同一批線程屬于同一“代”篮撑,當(dāng)所有線程都滿足了CyclicBarrier條件减细,執(zhí)行喚醒操作nextGeneration()方法時(shí),會(huì)新new 出一個(gè)Generation咽扇,代表一下“代”邪财。

nextGeneration的源碼
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();//調(diào)用Condition的signalAll方法,喚醒所有await的線程
        // set up next generation
        count = parties;//重置count值
        //生成新的Generation质欲,表示上一代的所有線程已經(jīng)喚醒,進(jìn)行更新?lián)Q代
        generation = new Generation(); 
    }
breakBarrier源碼

再來看一下breakBarrier的代碼糠馆,breakBarrier方法是在當(dāng)前線程被中斷時(shí)執(zhí)行的嘶伟,用來喚醒所有的等待線程:

    private void breakBarrier() {
        generation.broken = true;//表示當(dāng)代因?yàn)榫€程被中斷,已經(jīng)發(fā)成損壞了
        count = parties;//重置count值
        trip.signalAll();//調(diào)用Condition的signalAll方法又碌,喚醒所有await的線程
    }
isBroken方法
    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

判斷此屏障是否處于中斷狀態(tài)九昧。如果因?yàn)闃?gòu)造或最后一次重置而導(dǎo)致中斷或超時(shí),從而使一個(gè)或多個(gè)參與者擺脫此屏障點(diǎn)毕匀,或者因?yàn)楫惓6鴮?dǎo)致某個(gè)屏障操作失敗铸鹰,則返回true;否則返回false皂岔。

reset方法
    //將屏障重置為其初始狀態(tài)蹋笼。
    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //喚醒所有等待的線程繼續(xù)執(zhí)行,并設(shè)置屏障中斷狀態(tài)為true
            breakBarrier();   // break the current generation
            //喚醒所有等待的線程繼續(xù)執(zhí)行,并設(shè)置屏障中斷狀態(tài)為false
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }
getNumberWaiting方法
    //返回當(dāng)前在屏障處等待的參與者數(shù)目剖毯,此方法主要用于調(diào)試和斷言圾笨。
    public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }

總結(jié):
1.CyclicBarrier可以用于多線程計(jì)算數(shù)據(jù),最后合并計(jì)算結(jié)果的應(yīng)用場(chǎng)景逊谋。
2.這個(gè)等待的await方法擂达,其實(shí)是使用ReentrantLock和Condition控制實(shí)現(xiàn)的。
3.CyclicBarrier可以重復(fù)使用胶滋。

特別感謝

CyclicBarrier的原理分析和使用
Java并發(fā)編程:CountDownLatch板鬓、CyclicBarrier和Semaphore

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市究恤,隨后出現(xiàn)的幾起案子俭令,更是在濱河造成了極大的恐慌,老刑警劉巖丁溅,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唤蔗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡窟赏,警方通過查閱死者的電腦和手機(jī)妓柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涯穷,“玉大人棍掐,你說我怎么就攤上這事】娇觯” “怎么了作煌?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)赚瘦。 經(jīng)常有香客問我粟誓,道長(zhǎng),這世上最難降的妖魔是什么起意? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任鹰服,我火速辦了婚禮,結(jié)果婚禮上揽咕,老公的妹妹穿的比我還像新娘悲酷。我一直安慰自己,他們只是感情好亲善,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布设易。 她就那樣靜靜地躺著,像睡著了一般蛹头。 火紅的嫁衣襯著肌膚如雪顿肺。 梳的紋絲不亂的頭發(fā)上戏溺,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音挟冠,去河邊找鬼于购。 笑死,一個(gè)胖子當(dāng)著我的面吹牛知染,可吹牛的內(nèi)容都是我干的肋僧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼控淡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嫌吠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掺炭,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤辫诅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涧狮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炕矮,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年者冤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肤视。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涉枫,死狀恐怖邢滑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情愿汰,我是刑警寧澤困后,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站衬廷,受9級(jí)特大地震影響摇予,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吗跋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一趾盐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧小腊,春花似錦、人聲如沸久窟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斥扛。三九已至入问,卻和暖如春丹锹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芬失。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工楣黍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棱烂。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓租漂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親颊糜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哩治,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 此篇博客所有源碼均來自JDK 1.8 CyclicBarrier,一個(gè)同步輔助類衬鱼,在API中是這么介紹的:它允許一...
    chenssy閱讀 1,002評(píng)論 0 9
  • 一 用Runnable還是Thread业筏? Java中實(shí)現(xiàn)多線程有兩種方法:繼承Thread類、實(shí)現(xiàn)Runnable...
    Q南南南Q閱讀 3,730評(píng)論 0 4
  • Java-Review-Note——4.多線程 標(biāo)簽: JavaStudy PS:本來是分開三篇的鸟赫,后來想想還是整...
    coder_pig閱讀 1,646評(píng)論 2 17
  • 這個(gè)男人蒜胖,便是上個(gè)世紀(jì)90年代香港銅鑼灣的話事人陳浩南。熱血青春的沖勁和為兄弟兩肋插刀的仗義抛蚤,曾讓我效仿台谢,也成為了...
    浦大魔王76閱讀 258評(píng)論 4 3
  • ?我知道你在關(guān)心,我們就是老朋友了霉颠,你愿意戒煙嗎对碌?我希望我能做到,你不想去也是因?yàn)閾?dān)心自己做不到嗎蒿偎?是的朽们,你知道我...
    海哥0001閱讀 162評(píng)論 0 0