CyclicBarrier原始碼分析
CyclicBarrier的作用是讓一組執行緒互相等待至某個狀態後並行執行(相對外部來說是並行,其實內部還是序列)
基本的使用方法是建立一個CyclicBarrier例項,並且指定parties的個數,然後執行緒依次呼叫CyclicBarrier的await()方法讓自己進入等待狀態,當最後一個執行緒進入await()方法時,將會喚醒所有正在等待的執行緒,並行執行。
CyclicBarrier雖然也是同步器,但是並非直接通過AQS來進行實現的,而是藉助了ReentrantLock以及Condition來進行實現。
CyclicBarrier的結構
public class CyclicBarrier {
/**
* 存在一個Generation靜態內部類
*/
private static class Generation {
boolean broken = false; // 標識CyclicBarrier是否被破壞
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition trip = lock.newCondition(); // 與ReentrantLock繫結的Condition例項
private final int parties; // 用於記錄一共有多少個執行緒需要等待
private final Runnable barrierCommand; // 由最後一個進入await()方法的執行緒進行呼叫
private Generation generation = new Generation();
private int count; // 用於記錄還需要多少個執行緒進行等待
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);
}
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));
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// ......
}
// 其他省略
}
可以看到CyclicBarrier存在全域性的lock、trip、parties、count、barrierCommand以及generation屬性,其中parties屬性用於記錄一共有多少個執行緒需要等待,而count用於記錄還需要多少個執行緒進行等待。
同時CyclicBarrier中定義了一個Generation靜態內部類,該內部類只有一個broken全域性屬性,用於標識CyclicBarrier是否被破壞,預設為false。
同時CyclicBarrier的構造方法會初始化全域性的parties、count以及barrierCommand屬性(CyclicBarrier初始化後,count的數量等於parties的數量)
await()方法
由於當建立CyclicBarrier例項之後,執行緒需要依次呼叫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));
}
await()方法存在兩個過載,區別是一個支援超時,一個不支援超時,最終都會呼叫dowait()方法(使用timed參數列示是否有超時限制,如果timed引數為true則需要傳遞具體的超時時間)
dowait()方法
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 獲取全域性的ReentrantLock例項,並進行加鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 獲取全域性的Generation例項,如果Generation中的broken屬性為true則表示CyclicBarrier已經被破壞,則直接丟擲異常(預設是false)
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
// 如果執行緒已經被設定了中斷標識,則呼叫breakBarrier()方法,破壞CyclicBarrier
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException(); // 丟擲異常
}
// index屬性用於記錄還需要多少個執行緒進行等待
int index = --count;
// 如果index等於0,表示當前執行緒是最後一個進入await()方法的執行緒,如果barrierCommand不為空,那麼執行barrierCommand的run()方法,然後呼叫nextGeneration()方法,喚醒在指定Condition例項中等待的所有執行緒,並重置CyclicBarrier,然後執行緒直接返回,做自己的事情
if (index == 0) { // 最後一個執行緒走這個邏輯
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
// 如果在執行barrierCommand的run()方法時丟擲異常,那麼ranAction標識為false,那麼需要呼叫breakBarrier()方法,破壞CyclicBarrier
if (!ranAction)
breakBarrier();
}
}
// 如果非最後一個執行緒那麼將會往下執行
// 迴圈
for (;;) {
try {
// 如果沒有超時限制,那麼直接呼叫Condition例項的await()方法,讓執行緒在指定的Condition例項中進行等待,並釋放掉它擁有的鎖
// 如果有超時限制,那麼呼叫Condition例項的awaitNanos()方法,至多讓執行緒在指定的Condition例項中等待指定的時間,該方法返回執行緒被喚醒後剩餘的毫秒數(超時返回小於等於0),並釋放掉它擁有的鎖
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
// 當執行緒被喚醒後將會序列執行以下的邏輯
// 如果發現CyclicBarrier被破壞了,那麼就丟擲異常
if (g.broken)
throw new BrokenBarrierException();
// 正常情況下,當呼叫了nextGeneration()方法之後,generation引用就指向一個新的Generation例項,因此g!=generation,那麼執行緒直接返回,做自己的事情
if (g != generation)
return index;
// 如果執行緒在Condition例項等待的過程中由於達到了超時時間而被喚醒了,那麼將會呼叫breakBarrier()方法,破壞CyclicBarrier
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException(); // 丟擲異常
}
}
} finally {
lock.unlock(); // 解鎖
}
}
當執行緒進入dowait()方法後,需要獲取鎖,如果當前執行緒並非最後一個進入await()方法的執行緒,那麼將會在指定的Condition例項中進行等待,然後釋放掉它擁有的鎖,如果當前執行緒是最後一個進入await()方法的執行緒(index==0,表示還需要0個執行緒進行等待),如果barrierCommand不為空,那麼將會執行barrierCommand的run()方法,最後呼叫nextGeneration()方法。
如果在執行dowait()方法的過程中,執行緒已經被設定了中斷標識,或者最後一個執行緒在執行barrierCommand的run()方法時丟擲異常,或者在指定Condition例項等待的執行緒由於達到了超時時間而被喚醒,那麼都會呼叫breakBarrier()方法。
nextGeneration()方法
private void nextGeneration() {
// 喚醒在指定Condition例項中等待的所有執行緒
trip.signalAll();
// 將count的數量設定成parties
count = parties;
// 將generation引用指向一個新的Generation例項
generation = new Generation();
}
nextGeneration()方法用於指向下一個Generation,該方法將會喚醒在指定Condition例項中等待的所有執行緒,然後將count的數量設定成parties,恢復成CyclicBarrier初始化後的狀態,最後將generation引用指向一個新的Generation例項。
breakBarrier()方法
private void breakBarrier() {
// 將Generation例項的broken屬性設定為true,表示CyclicBarrier已經被破壞
generation.broken = true;
// 將count的數量設定回parties
count = parties;
// 喚醒在指定Condition例項中等待的所有執行緒
trip.signalAll();
}
breakBarrier()方法用於破壞CyclicBarrier,將Generation例項的broken屬性設定為true,表示CyclicBarrier已經被破壞,然後將count的數量設定成parties,最後喚醒在指定Condition例項中等待的所有執行緒。
reset()方法
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // 感覺是多餘的
nextGeneration(); // 指向下一個Generation
} finally {
lock.unlock();
}
}
reset()方法用於重置CyclicBarrier,其根本是將generation引用指向一個新的Generation例項。
流程總結
1.當建立了一個CyclicBarrier例項之後,執行緒需要依次呼叫CyclicBarrier的await()方法,讓自己進入等待狀態。
2.await()方法又會呼叫dowait()方法,當執行緒進入dowait()方法後,需要獲取鎖,如果當前執行緒並非最後一個進入await()方法的執行緒,那麼將會在指定的Condition例項中進行等待,然後釋放掉它擁有的鎖,如果當前執行緒是最後一個進入await()方法的執行緒(index==0,表示還需要0個執行緒進行等待),如果barrierCommand不為空,那麼將會執行barrierCommand的run()方法,最後呼叫nextGeneration()方法。
3.nextGeneration()方法用於指向下一個Generation,該方法將會喚醒在指定Condition例項中等待的所有執行緒,然後將count的數量設定成parties,恢復成CyclicBarrier初始化後的狀態,最後將generation引用指向一個新的Generation例項,當最後一個執行緒執行完nextGeneration()方法後,將會直接返回,做自己的事情,最後釋放掉它擁有的鎖。
4.當被喚醒的執行緒依次獲取到鎖後,將會繼續往下執行,如果判斷到generation引用已經指向一個新的Generation例項,那麼直接返回,做自己的事情,最後釋放掉它擁有鎖。
5.如果在執行dowait()方法的過程中,執行緒已經被設定了中斷標識,或者最後一個執行緒在執行barrierCommand的run()方法時丟擲異常,或者在指定Condition例項等待的執行緒由於達到了超時時間而被喚醒,那麼都會呼叫breakBarrier()方法,破壞CyclicBarrier,將Generation例項的broken屬性設定為true,表示CyclicBarrier已經被破壞,然後將count的數量設定成parties,最後喚醒在指定Condition例項中等待的所有執行緒。
6.當被喚醒的執行緒依次獲取到鎖後,將會繼續往下執行,如果判斷到Generation例項的broken屬性被設定了true,也就是CyclicBarrier已經被破壞,那麼將會直接丟擲異常,最後釋放掉它擁有的鎖。
7.當CyclicBarrier被破壞後是不能夠進行復用的,因為Generation的broken屬性已經被設定成true,因此需要先呼叫一次reset()方法進行重置。
FAQ
為什麼說CyclicBarrier是可以複用的?
因為當最後一個執行緒進入await()方法,將會呼叫nextGeneration()方法,該方法除了喚醒在指定Condition中等待的執行緒之外,還會將count的數量設定成parties,恢復成CyclicBarrier初始化後的狀態,同時將generation引用指向一個新的Generation例項,因此CyclicBarrier是可以複用的,同時需要注意的是,如果CyclicBarrier已經被破壞,那麼需要先呼叫一次reset()方法之後才能夠進行復用。