Java併發——同步屏障
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達到屏障放行標準後放行的是任務執行緒,並且還會額外地觸發一個達到標準後執行的響應執行緒;