繼續(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)目中使用過。
