前言
本篇將分析CyclicBarrier的源碼,分析結束後,會用一個示例展示CyclicBarrier,並比較CyclicBarrier和CountDownLatch的區別。
1、CyclicBarrier的簡介
CyclicBarrier允許一組線程在觸發屏障之前相互等待,直到達到某一條件才繼續執行;CyclicBarrier在這些線程釋放後,又可以重新使用,所以也稱循環柵欄。
2、分析源碼
2.1、構造方法
//定義在觸發屏障之前必須調用的線程數
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
//定義最後一個線程到達屏障時執行的操作
this.barrierCommand = barrierAction;
}
2.2、await()方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
//調用dowait方法,不需要定義超時時間
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
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()) {
//終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程
breakBarrier();
throw new InterruptedException();
}
//計數
int index = --count;
//達到屏障(最後一個線程到達)
//最後一個到達的線程不執行下面的for循環語句
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
//執行初始化傳入的命令操作
command.run();
ranAction = true;
//調用下一代方法
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
//非最後一個到達的線程全部執行此語句,阻塞在trip.await()方法上
for (;;) {
try {
//如果不是“超時等待”
if (!timed)
// 調用condition的await()方法
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
// 如果等待過程中,線程被中斷,則執行下面的函數
if (g == generation && ! g.broken) {
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();
}
}
if (g.broken)
throw new BrokenBarrierException();
// 如果“generation換代了”,則返回index。
if (g != generation)
return index;
//檢查超時
if (timed && nanos <= 0L) {
// 如果超時,則終止CyclicBarrier,並喚醒CyclicBarrier中所有等待線程
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//釋放“獨佔鎖”,並喚醒AQS中等待的下一個線程
lock.unlock();
}
}
private void nextGeneration() {
// 調用condition的signalAll()將其隊列中的等待者全部轉移到AQS的隊列中
trip.signalAll();
// 重置count
count = parties;
// 進入下一代
generation = new Generation();
}
3、CyclicBarrier與CountDownLatch的區別
- 兩者都能實現阻塞一組線程,然後等待喚醒;
- 前者是最後一個線程到達直接喚醒,後者是調用countDown()方法;
- 前者是通過ReentrantLock的"獨佔鎖"和Conditon來實現,後者是通過AQS的“共享鎖”實現;
- 前者可以重複使用,後者只能使用一次;
- 前者只能實現多個線程到達後一起運行(多個條件成立才能一起運行);
- 後者不僅可以實現一個線程等待多個線程(多個條件成立才能一起運行),還能實現多個線程等待一個線程(多個條件成立並且等待某個特殊信號才能一起運行)
4、示例
public class CyclicBarrierTest {
public static final CyclicBarrier WORK_THREAD = new CyclicBarrier(3);
public static void main(String[] args) throws Exception{
//主線程邏輯
Thread.sleep(2000);
for (int i=0; i<3; i++){
String condition_name = "條件"+i;
new Thread(() -> { method(); },condition_name).start();
}
}
public static void method(){
System.out.println("等待的條件是:" + Thread.currentThread().getName());
try {
WORK_THREAD.await();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("執行完的條件是:" + Thread.currentThread().getName());
}
}
輸出結果:
等待的條件是:條件0
等待的條件是:條件1
等待的條件是:條件2
執行完的條件是:條件1
執行完的條件是:條件2
執行完的條件是:條件0
結束語
本篇介紹了CyclicBarrier,分析了源碼,用了一段代碼演示了使用方法,並比較了CyclicBarrier和CountDownLatch的區別。
JUC鎖篇章到此就分析完了,下一篇將開啓分析JUC集合。