多線程同步【2】之CyclicBarrier

繼續(xù)總結(jié)多線程同步常用的方法或者類,上一節(jié)介紹了CountDownLatch,這次介紹一下它的加強(qiáng)版本CyclicBarriar。

CyclicBarriar--循環(huán)柵欄

CyclicBarriar的一個(gè)特別典型的應(yīng)用場(chǎng)景是:有一個(gè)比較大型的任務(wù),需要分配好多個(gè)人分多個(gè)階段去執(zhí)行,在每個(gè)階段,需要每個(gè)人都參與,并且需要所有人在完成各自的子任務(wù)后才算完成這個(gè)階段的工作,才能開始下一個(gè)階段的子任務(wù),最后所有階段工作都完成后,才能執(zhí)行主任務(wù),這時(shí)候,就可以選擇CyclicBarrier了。

1、CyclicBarriar的定義

CyclicBarrier也是在Java1.5中被引入的一個(gè)線程同步類。CyclicBarrier類似于CountDownLatch也是個(gè)計(jì)數(shù)器, 不同的是CyclicBarrier初始元素個(gè)數(shù)是調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù), 當(dāng)線程數(shù)達(dá)到了CyclicBarrier初始時(shí)規(guī)定的數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù),如此循環(huán)。

CyclicBarrier就象它名字的意思一樣,可看成是個(gè)障礙。它允許一組線程互相等待,直到到達(dá)某個(gè)公共柵欄(屏障)點(diǎn) (common barrier point)。在涉及需要多次并且多個(gè)線程進(jìn)行互相等待時(shí),所有的線程必須到齊后才能一起通過這個(gè)障礙點(diǎn),CyclicBarrier將會(huì)非常有用。

2、基本元素和常用方法

CyclicBarrier(int parties)

? ? ? ? ? 創(chuàng)建一個(gè)新的CyclicBarrier,parties表示有多少個(gè)數(shù)量的參與者參與。

CyclicBarrier(int parties, Runnable barrierAction)

? ? ? ? ? 創(chuàng)建一個(gè)新的CyclicBarrier,parties表示有多少個(gè)數(shù)量的參與者參與。barrierAction會(huì)由最后一個(gè)進(jìn)入 barrier 的參與者執(zhí)行。

int await()

? ? ? ? ? 在所有參與者在調(diào)用 await方法之前,將一直等待。

int await(long timeout, TimeUnit unit)

? ? ? ? ? 在等待時(shí)間超過timeout之前,所有參與者在調(diào)用 await方法之前,將一直等待。unit表示等待時(shí)間的單位。

int getNumberWaiting()

? ? ? ? ? 返回當(dāng)前在屏障處等待的參與者數(shù)目。

int getParties()

? ? ? ? ? 返回要求啟動(dòng)此barrier的參與者數(shù)目。

boolean isBroken()

? ? ? ? ? 查詢此屏障是否處于損壞狀態(tài)。

void reset()

? ? ? ? ? 將屏障重置為其初始狀態(tài)。

CyclicBarrier 類構(gòu)造函數(shù)CyclicBarrier(int parties)有一個(gè)整數(shù)初始值,這個(gè)值表示將在同一個(gè)點(diǎn)需要同步的線程數(shù)量。當(dāng)其中一個(gè)線程到達(dá)某個(gè)階段點(diǎn)后,它會(huì)調(diào)用await() 方法來等待其他線程。調(diào)用這個(gè)方法后,CyclicBarrier阻塞線程進(jìn)入休眠直到其他線程到達(dá)。當(dāng)最后一個(gè)線程調(diào)用CyclicBarrier 類的await() 方法,它喚醒所有等待的線程并繼續(xù)執(zhí)行它們的任務(wù)。然后如此循環(huán)。

CyclicBarrier 類的另一個(gè)構(gòu)造函數(shù)CyclicBarrier(int parties, Runnable barrierAction)初始時(shí)還可帶一個(gè)Runnable的參數(shù),此Runnable任務(wù)在CyclicBarrier的數(shù)目達(dá)到后,所有其它線程被喚醒前被執(zhí)行。

3、演示代碼

import java.util.concurrent.BrokenBarrierException;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class TestCyclicBarrier {

? ? public static void main(String [] args){

? ? ? ? ExecutorService service= Executors.newCachedThreadPool();

? ? ? ? final CyclicBarrier cb=new CyclicBarrier(3);? //三個(gè)線程同時(shí)到達(dá)

? ? ? ? MyRunnable myRunnable1 = new MyRunnable("張三", cb);

? ? ? ? service.execute(myRunnable1);

? ? ? ? MyRunnable myRunnable2 = new MyRunnable("趙四", cb);

? ? ? ? service.execute(myRunnable2);

? ? ? ? MyRunnable myRunnable3 = new MyRunnable("李五", cb);

? ? ? ? service.execute(myRunnable3);

? ? ? ? service.shutdown();

? ? }

? ? public void reachSchedule(String name, CyclicBarrier cb){

? ? ? ? try{

? ? ? ? ? ? Thread.sleep((long)(Math.random()*10000));

? ? ? ? ? ? System.out.println(name+

? ? ? ? ? ? ? ? ? ? "到達(dá)公園,當(dāng)前共有"+(cb.getNumberWaiting()+1)+"個(gè)已到達(dá)"+

? ? ? ? ? ? ? ? ? ? (cb.getNumberWaiting()==2 ? ",到齊了,然后向公園門口出發(fā)!":"正在等候"));

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? cb.await();

? ? ? ? ? ? } catch (BrokenBarrierException e) {

? ? ? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? public void reachPark(String name, CyclicBarrier cb){

? ? ? ? try{

? ? ? ? ? ? Thread.sleep((long)(Math.random()*10000));

? ? ? ? ? ? System.out.println(name+

? ? ? ? ? ? ? ? ? ? "到達(dá)公園,當(dāng)前共有"+(cb.getNumberWaiting()+1)+"個(gè)已到達(dá)"+

? ? ? ? ? ? ? ? ? ? (cb.getNumberWaiting()==2 ? ",都到公園了,發(fā)票開始玩,然后向飯店出發(fā)!":"正在等候"));

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? cb.await();

? ? ? ? ? ? } catch (BrokenBarrierException e) {

? ? ? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? public void reachHotel(String name, CyclicBarrier cb){

? ? ? ? try{

? ? ? ? ? ? Thread.sleep((long)(Math.random()*10000));

? ? ? ? ? ? System.out.println(name+

? ? ? ? ? ? ? ? ? ? "到達(dá)公園,當(dāng)前共有"+(cb.getNumberWaiting()+1)+"個(gè)已到達(dá)"+

? ? ? ? ? ? ? ? ? ? (cb.getNumberWaiting()==2 ? ",都到飯店了,開始吃飯!":"正在等候"));

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? cb.await();

? ? ? ? ? ? } catch (BrokenBarrierException e) {

? ? ? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? static class MyRunnable implements Runnable {

? ? ? ? private String name;

? ? ? ? private CyclicBarrier cb;

? ? ? ? MyRunnable(String name, CyclicBarrier cb){

? ? ? ? ? ? this.name = name;

? ? ? ? ? ? this.cb = cb;

? ? ? ? }


? ? ? ? @Override

? ? ? ? public void run() {

? ? ? ? ? ? //先到學(xué)校

? ? ? ? ? ? reachSchedule(name);

? ? ? ? ? ? //再到公園

? ? ? ? ? ? reachPark(name);

? ? ? ? ? ? //最后到飯店

? ? ? ? ? ? reachHotel(name);

? ? ? ? }

? ? }

}

運(yùn)行結(jié)果:

趙四到達(dá)學(xué)校,當(dāng)前共有1個(gè)已到達(dá),等著吧。

張三到達(dá)學(xué)校,當(dāng)前共有2個(gè)已到達(dá),等著吧。

李五到達(dá)學(xué)校,當(dāng)前共有3個(gè)已到達(dá),到齊了,然后向公園門口出發(fā)!

張三到達(dá)公園,當(dāng)前共有1個(gè)已到達(dá),等著吧。

李五到達(dá)公園,當(dāng)前共有2個(gè)已到達(dá),等著吧。

趙四到達(dá)公園,當(dāng)前共有3個(gè)已到達(dá),都到公園了,發(fā)票開始玩,結(jié)束后向飯店出發(fā)!

趙四到達(dá)旅館,當(dāng)前共有1個(gè)已到達(dá),等著吧。

李五到達(dá)旅館,當(dāng)前共有2個(gè)已到達(dá),等著吧。

張三到達(dá)旅館,當(dāng)前共有3個(gè)已到達(dá),都到飯店了,開始吃飯!

4、CountDownLatch和CyclicBarrier比較

CountDownLatch的作用是允許1或N個(gè)線程等待其他線程完成執(zhí)行;而CyclicBarrier則是允許N個(gè)線程相互等待。

CountDownLatch的計(jì)數(shù)器無法被重置,只能作為一次性的barrier使用;而CyclicBarrier的計(jì)數(shù)器可以被重置繼續(xù)使用,因此它被稱為是循環(huán)的barrier。

CyclicBarrier可以替換CountDownLatch來使用,但是反過來行不通。

5、總結(jié)

如果是簡單的一次性的多個(gè)或者一個(gè)線程同步,那使用CountDownLatch會(huì)很方便,本司機(jī)在日常開發(fā)中經(jīng)常使用CountDownLatch處理兩個(gè)線程的同步問題,真的是比較方便。如果是多個(gè)線程并需要多次同步時(shí),可以考慮使用CyclicBarrier,目前本司機(jī)還沒有在實(shí)際項(xiàng)目中使用過。


本公眾號(hào)將以推送Android各種碎片化小知識(shí)或小技巧,以及整理老司機(jī)日常工作中踩過的坑涉及到的知識(shí)點(diǎn)為主,也會(huì)不定期將正在學(xué)習(xí)使用的React Native一些知識(shí)點(diǎn)總結(jié)出來進(jìn)行分享。每天一點(diǎn)干貨小知識(shí)把你的碎片時(shí)間充分利用起來。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容