CyclicBarrier
阻塞一組線程直到某件事發生,可重複使用,另一種柵欄式。5個人約好集合後去其他地方。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
//屏障攔截的線程數量
this.parties = parties;
//線程數量
this.count = parties;
//指定個數線程被wait後,執行的後續方法
this.barrierCommand = barrierAction;
}
await
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
```java
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()) {
breakBarrier();
throw new InterruptedException();
}
//獲取下標
int index = --count;
//說明最後一個調用了
if (index == 0) { // tripped
boolean ranAction = false;
try {
//獲取柵欄任務
final Runnable command = barrierCommand;
if (command != null)
//執行
command.run();
ranAction = true;
//重新構造可wait線程數
//以及換代
nextGeneration();
return 0;
} finally {
//非正常退出
if (!ranAction)
//標記柵欄損壞
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) {
//柵欄損壞
breakBarrier();
throw ie;
} else {
//說明這個線程不是當代的,就不會影響當代執行,則中斷線程
//如果在等待過程中,線程被中斷了,就拋出異常。但如果中斷的線程所對應的CyclicBarrier不是這代的,
// 比如,在最後一次線程執行signalAll後,並且更新了這個“代”對象。在這個區間,
// 這個線程被中斷了,那麼,JDK認爲任務已經完成了,就不必在乎中斷了,只需要打個標記。
// 該部分源碼已在dowait(boolean, long)方法中進行了註釋
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
//表示正常換代了,返回當前線程所在下標,便於下次做選舉等操作
//仔細發現,柵欄不是一次性的,可以重複使用多次,但是鎖是同一個,所以需要用generation進行區分,是否是同一個柵欄
if (g != generation)
return index;
//超時,標記柵欄損壞,且拋出異常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//釋放鎖
lock.unlock();
}
}
private void nextGeneration() {
// signal completion of last generation
//喚醒所有線程
trip.signalAll();
// set up next generation
//count恢復parties個數
count = parties;
//換代
generation = new Generation();
}
private void breakBarrier() {
//標記柵欄損壞
generation.broken = true;
//count恢復parties個數
count = parties;
//喚醒所有線程
trip.signalAll();
}
reset
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
總結:可重複使用多次,底層採用顯示鎖進行加鎖,
索引按照初始count遞減,當索引爲0時,執行柵欄任務
當執行成功,通知當代所有線程,接着換代,如果當代有線程中斷了,且已經執行了換代任務了,這個時候會將中斷線程標記中斷