一、CyclicBarrier是什麼?
源代碼的解釋是:
A synchronization aid that allows a set of threads to all wait for
each other to reach a common barrier point. CyclicBarriers are
useful in programs involving a fixed sized party of threads that
must occasionally wait for each other. The barrier is called
<em>cyclic</em> because it can be re-used after the waiting threads
其實核心就一句話:
當線程達到初始化的數量時,才能進入到下一步的操作。
怎麼理解,舉個栗子:
你和你的幾個朋友相約去吃飯,但是餐廳有個要求,等你的幾個朋友到全了,並且滿足了餐廳位子情況下,你們才能計入餐廳就餐。這裏的朋友們就是各個線程,餐廳就是 CyclicBarrier。
二、CyclicBarrier源碼
1、構造方法
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
核心參數解釋:
parties the number of threads that must invoke {@link #await} before the barrier is tripped
barrierAction the command to execute when the barrier is tripped, or {@code null} if there is no action
2、核心方法:
Waits until all {@linkplain #getParties parties} have invoked {@code await} on this barrier.
調用await方法的線程告訴CyclicBarrier自己已經到達同步點,然後當前線程被阻塞。直到parties個參與線程調用了await方法,CyclicBarrier同樣提供帶超時時間的await和不帶超時時間的await方法:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
這兩個方法最終都會調用dowait(boolean, long)方法,它也是CyclicBarrier的核心方法:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//獲取獨佔鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
//當前代
final Generation g = generation;
//如果當前代白破壞,就報錯
if (g.broken)
throw new BrokenBarrierException();
//如果線程中斷,就報錯
if (Thread.interrupted()) {
//將中斷的線程設置爲true,並同時其他阻塞的線程這個線程中斷
breakBarrier();
throw new InterruptedException();
}
//獲取線程的下標
int index = --count;
//如果下標爲0,說明最後一個線程調用了這個方法
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//執行任務
if (command != null)
command.run();
//執行完成
ranAction = true;
//更新新的一代,重置count,喚醒新的線程
nextGeneration();
return 0;
} finally {
//如果執行出錯,將 損壞的狀態設置爲true
if (!ranAction)
//將中斷的線程設置爲true,並同時其他阻塞的線程這個線程中斷
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//如果沒有時間限制,則等待
if (!timed)
trip.await();
//否則根據設置的等待時間進行等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//當前代際沒有損壞
if (g == generation && ! g.broken) {
//將中斷的線程設置爲true,並同時其他阻塞的線程這個線程中斷
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
//上面條件不滿足,說明這個線程不是這代的
//就不會影響當前這代柵欄的執行,所以,就打個中斷標記
Thread.currentThread().interrupt();
}
}
//如果任何線程中斷了,就會調用breakBarrier
//此時就會喚醒起他線程,其他線程被喚醒時候也會拋出BrokenBarrierException異常
//表示線程又中斷
if (g.broken)
throw new BrokenBarrierException();
//如果正常換代了,那麼就會返回當前線程的下標
if (g != generation)
return index;
//如果超時或者時間小等於0了,那麼就會報錯TimeoutException,超時
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//釋放鎖
lock.unlock();
}
}
三、CyclicBarrier基本使用
//3個人聚餐
final CyclicBarrier cb =new CyclicBarrier(3, new Runnable() {
public void run() {
System.out.println("人員全部到齊了,拍照留念。。。");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//線程池
ExecutorService threadPool= Executors.newCachedThreadPool();
//模擬3個用戶
for (int i = 0;i < 20; i++) {
final int user =i+1;
Runnable r=new Runnable() {
public void run() {
//模擬每個人來的時間不一樣
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println(user+"到達聚餐點,當前已有"+(cb.getNumberWaiting()+1)+"人達到");
//阻塞
cb.await();
if(user==1){ //打印一句
System.out.println("拍照結束,開始吃飯...");
}
Thread.sleep((long)(Math.random()*10000));
System.out.println(user+"吃完飯..準備回家.");
}catch (InterruptedException e){
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
threadPool.execute(r);
}
threadPool.shutdown();
}