1.同步屏障
同步屏障允許一組線程彼此相互等待,直到抵達某個公共的屏障點。它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續幹活。
舉個簡單的例子就是:旅遊團帶着一幫人蔘觀景點,規定在下一個景點A處集合,於是導遊就在景點A等着大家,導遊就是這個集合點或者說屏障,直到所有的遊客集合完畢,導遊纔會帶着大家繼續參觀下一個景點B.
類java.util.concurrent.CyclicBarrier實現了同步屏障。
2. 同步屏障適用場景
CyclicBarrier可以用於多線程計算數據,最後合併計算結果的場景。
3.常用方法
(1)CyclicBarrier(int parties)構造函數
初始化一個包含指定parties數目的CyclicBarrier實例。如果parties的值小於1,構造函數就會拋出IllegalArgumentException。
(2)CyclicBarrier(int parties, Runnable barrierAction)構造函數
初始化一個包含指定parties數目的線程以及一旦跨越屏障就會執行的barrierAction. 也就是說當最後一個線程到達一個屏障點時,就會馬上執行barrierAction. 這個Runnable適用於在任意線程繼續執行之前更新共享狀態。
如果parties的值小於1,構造函數就會拋出IllegalArgumentException。若把barrierAction設爲null,那麼當跨越屏障時,就沒有runnable可供執行。
(3)int await()
每個線程調用await(),表示我已經到達屏障點,然後當前線程被阻塞。如果調用線程是最後一條到達的線程,並且構造函數中提供了一個非空的barrierAction,這條線程就會在允許其他線程繼續執行之前率先執行這個runnable。該方法會返回調用線程的到達索引,getParties-1代表第一條到達的線程,0代表最後一條到達的線程。
(4)int await(long timeout, TimeUnit unit)
指定調用線程願意等待的時長,其他的跟上一個方法相同。當線程在等待中超時,該方法會拋出java.util.concurrent.TimeoutException。
(5)int getNumberWaiting()
返回當前在同步屏障上等待的線程數目。
(6)int getParties()
返回需要跨越同步屏障的線程數目。
(7)boolean isBroken()
當一條或多條線程由於在同步屏障創建或上次重置之後,中斷或超時從而打破同步屏障,又或者因爲一個異常導致barrier action失敗時,返回true;否則返回false。
(8)void reset()
把同步屏障重置到其原始狀態。
4.示例
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
public static void main(String[] args)
{
CyclicBarrier barrier = new CyclicBarrier(3);
Runnable r = new Runnable() {
@Override
public void run()
{
try {
Thread.sleep(new Random().nextInt(10000));
String name = Thread.currentThread().getName();
System.out.println(name + "即將到達,當前已有"+(barrier.getNumberWaiting()+1)+"條線程已經到達!"+(barrier.getNumberWaiting()==2?"都到齊了,繼續走":"正在等待"));
barrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0; i < 3; i++)
{
executorService.submit(r);
}
executorService.shutdown();
}
}
運行結果:
pool-1-thread-1即將到達,當前已有1條線程已經到達!正在等待
pool-1-thread-2即將到達,當前已有2條線程已經到達!正在等待
pool-1-thread-3即將到達,當前已有3條線程已經到達!都到齊了,繼續走
5. CountDownLatch與CountDownLatch的比較
(1)CountDownLatch是把主幹線程掛起,在任務線程中進行倒數計數,直到任務線程執行完才喚醒主幹線程繼續執行;
CyclicBarrier是把任務線程掛起,直到所有任務線程執行到屏障處再放行繼續執行;
(2)CountDownLatch達到屏障放行標準後放行的是主幹線程;
CyclicBarrier達到屏障放行標準後放行的是任務線程,並且還會額外地觸發一個達到標準後執行的響應線程;