一CyclicBarrier
控制所有線程等待,知道達到某種條件,所有的線程纔會繼續走下去
- 是一個同步工具類
- 它允許一組線程互相等待,直到到達某個公共屏障點才停止等待
- 與CountDownLatch不同的是該barrier(屏障)在釋放等待線程後可以重用,所以稱它爲循環(Cyclic)的屏障(Barrier)。
- CyclicBarrier支持一個可選的Runnable命令,在一組線程中的最後一個線程到達之後(但在釋放所有線程之前),該命令只在每個屏障點運行一次。若在繼續所有參與線程之前更新共享狀態,此屏障操作很有用。
- 對於失敗的同步嘗試,CyclicBarrier 使用了一種要麼全部要麼全不 (all-or-none) 的破壞模式:如果因爲中斷、失敗或者超時等原因,導致線程過早地離開了屏障點,那麼在該屏障點等待的其他所有線程也將通過 BrokenBarrierException(如果它們幾乎同時被中斷,則用 InterruptedException)以反常的方式離開。
二方法
- CyclicBarrier(int parties, Runnable barrierAction):
- parties:屏障攔截的線程數量,當屏障撤銷時,先執行barrierAction,然後在釋放所有線程。
- CyclicBarrier(int parties): barrierAction默認爲null
- int await() :
- 當前線程等待直到所有線程都調用了該屏障的await()方法
- 如果當前線程不是將到達的最後一個線程,將會被阻塞。解除阻塞的情況有以下幾種
- 最後一個線程調用await()
- 當前線程被中斷
- 其他正在該CyclicBarrier上等待的線程被中斷
- 其他正在該CyclicBarrier上等待的線程超時
- 其他某個線程調用該CyclicBarrier的reset()方法
- 如果當前線程在進入此方法時已經設置了該線程的中斷狀態或者在等待時被中斷,將拋出InterruptedException,並且清除當前線程的已中斷狀態。
- 如果在線程處於等待狀態時barrier被reset()或者在調用await()時 barrier 被損壞,將拋出 BrokenBarrierException 異常。
- 如果任何線程在等待時被中斷,則其他所有等待線程都將拋出 BrokenBarrierException 異常,並將 barrier 置於損壞狀態。
- 如果當前線程是最後一個將要到達的線程,並且構造方法中提供了一個非空的屏障操作(barrierAction),那麼在允許其他線程繼續運行之前,當前線程將運行該操作。
- 如果在執行屏障操作過程中發生異常,則該異常將傳播到當前線程中,並將 barrier 置於損壞狀態。
- 返回值爲當前線程的索引,0表示當前線程是最後一個到達的線程
- int await(long timeout, TimeUnit unit) :
- 在await()的基礎上增加超時機制,如果超出指定的等待時間,則拋出 TimeoutException 異常。如果該時間小於等於零,則此方法根本不會等待
- void reset():將屏障重置爲其初始狀態。如果所有參與者目前都在屏障處等待,則它們將返回,同時拋出一個BrokenBarrierException。
int getnumberwaiting():返回當前在屏障處等待的參與者數目
3案例
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//屏障點3,表示需要3個線程都到屏障點纔可以繼續走下去,
final CyclicBarrier cb = new CyclicBarrier(3);
for(int i=0;i<3;i++){//測試的時候可以設置比3大的數
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*5000));
System.out.println(Thread.currentThread().getName() +"到地點1,當前已有" + (cb.getNumberWaiting()+1) + "個在等待。");
cb.await();
System.out.println("地點1已經有3個人到達了,可以先去下個地點了");
Thread.sleep((long)(Math.random()*5000));
System.out.println(Thread.currentThread().getName() +"到地點2,當前已有" + (cb.getNumberWaiting()+1) + "個在等待。");
cb.await();
System.out.println("地點2已經有3個人到達了,可以先去下個地點了");
Thread.sleep((long)(Math.random()*5000));
System.out.println(Thread.currentThread().getName() +"到地點3,當前已有" + (cb.getNumberWaiting()+1) + "個在等待。");
cb.await();
System.out.println("地點3已經有3個人到達了,可以先去下個地點了");
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
4結果分析
pool-1-thread-3到地點1,當前已有1個在等待。
pool-1-thread-1到地點1,當前已有2個在等待。
pool-1-thread-2到地點1,當前已有3個在等待。----解釋
多個線程都堵在地點1,知道線程數滿足大於等於屏障數才繼續走下去,否則會一直等待
----解釋
地點1已經有3個人到達了,可以先去下個地點了
地點1已經有3個人到達了,可以先去下個地點了
地點1已經有3個人到達了,可以先去下個地點了
pool-1-thread-1到地點2,當前已有1個在等待。
pool-1-thread-2到地點2,當前已有2個在等待。
pool-1-thread-3到地點2,當前已有3個在等待。
地點2已經有3個人到達了,可以先去下個地點了
地點2已經有3個人到達了,可以先去下個地點了
地點2已經有3個人到達了,可以先去下個地點了
pool-1-thread-1到地點3,當前已有1個在等待。
pool-1-thread-2到地點3,當前已有2個在等待。
pool-1-thread-3到地點3,當前已有3個在等待。
地點3已經有3個人到達了,可以先去下個地點了
地點3已經有3個人到達了,可以先去下個地點了
地點3已經有3個人到達了,可以先去下個地點了
這邊文章寫得非常好