案例
CyclicBarrier是一個同步輔助類,允許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。因爲該 barrier 在釋放等待線程後可以重用,所以稱它爲循環 的 barrier。
public class CyclicBarrierTest {
public static Map<String,Integer> result = new ConcurrentHashMap<>();
public static CyclicBarrier barrier = new CyclicBarrier(4,()->{
System.out.println("彙總任務啓動");
int rs = 0;
for(Map.Entry<String,Integer> entry:result.entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue());
rs += entry.getValue();
}
System.out.println("rs="+rs);
});
public static void main(String[] args) {
for(int i=0;i<4;i++){
Thread t = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"啓動");
try {
Random random = new Random();
result.put(Thread.currentThread().getName(),random.nextInt(10));
barrier.await();
System.out.println(Thread.currentThread().getName()+"喚醒後繼續執行");
// 再次執行barrier.await() 還會觸發彙總任務
// barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
打印結果
Thread-0啓動
Thread-1啓動
Thread-2啓動
Thread-3啓動
彙總任務啓動
Thread-3:4
Thread-0:1
Thread-1:8
Thread-2:5
rs=18
Thread-3喚醒後繼續執行
Thread-2喚醒後繼續執行
Thread-1喚醒後繼續執行
Thread-0喚醒後繼續執行
可以發現,喚醒的順序跟await的順序是相反的,這涉及到AQS裏面的機制。
Condition
object.wait()和object.notifyAll()實現了通知,需配合synchronized使用。
condition.await()和condition.signalAll()和上面類似,需配合ReentrantLock使用。
public class ConditionTest {
public static Lock lock = new ReentrantLock();
public static Condition cond1 = lock.newCondition();
public static Condition cond2 = lock.newCondition();
public static void main(String[] args) {
for(int i=0;i<3;i++) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
// 類似於object.wait() 釋放鎖,等待被喚醒然後繼續執行
if(Thread.currentThread().getId()==13){
System.out.println(Thread.currentThread().getName() + "獲得鎖,cond1.await()");
cond1.await();
}else{
System.out.println(Thread.currentThread().getName() + "獲得鎖,cond2.await()");
cond2.await();
}
System.out.println(Thread.currentThread().getName() + "被喚醒,且帶着鎖休眠1s");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 喚醒所有線程,首先得獲取鎖,signal後得釋放鎖
lock.lock();
cond1.signalAll();
System.out.println("喚醒cond1所有線程,同時釋放鎖");
lock.unlock();
}
}
可以喚醒指定的與condition綁定的線程
Thread-0獲得鎖,cond2.await()
Thread-1獲得鎖,cond2.await()
Thread-2獲得鎖,cond1.await()
喚醒cond1所有線程,同時釋放鎖
Thread-2被喚醒,且帶着鎖休眠1s
CyclicBarrier原理
具體邏輯實現是
- 定義變量 lock , trip , generation , barrierCommand , parties,count 等等
- 通過構造函數獲取必須同時到達barrier的線程個數(parties)和parties個線程到達barrier時,會執行的動作(barrierCommand)
- 如果未達到線程個數,則執行trip.await() 方法(即condition.await()),釋放鎖,等待被喚醒。count–
- 如果達到線程個數,執行barrierCommand,trip.signalAll() 喚醒線程並更新generation,parties,count,意味着新的一輪計數開始。
- 定義變量
private static class Generation {
boolean broken = false;
}
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
/**
* Number of parties still waiting. Counts down from parties to 0
* on each generation. It is reset to parties on each new
* generation or when broken.
*/
private int count;
- 構造函數
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// parties表示“必須同時到達barrier的線程個數”。
this.parties = parties;
// count表示“處在等待狀態的線程個數”。
this.count = parties;
// barrierCommand表示“parties個線程到達barrier時,會執行的動作”。
this.barrierCommand = barrierAction;
}
- await()
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
// 獲取“獨佔鎖(lock)”
lock.lock();
try {
// 保存“當前的generation”
final Generation g = generation;
// 若“當前generation已損壞”,則拋出異常。
if (g.broken)
throw new BrokenBarrierException();
// 如果當前線程被中斷,則通過breakBarrier()終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程。
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 將“count計數器”-1
int index = --count;
// 如果index=0,則意味着“有parties個線程到達barrier”。
if (index == 0) { // tripped
boolean ranAction = false;
try {
// 如果barrierCommand不爲null,則執行該動作。
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
// 喚醒所有等待線程,並更新generation。
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 當前線程一直阻塞,直到“有parties個線程到達barrier” 或 “當前線程被中斷” 或 “超時”這3者之一發生,
// 當前線程才繼續執行。
for (;;) {
try {
// 如果不是“超時等待”,則調用awati()進行等待;否則,調用awaitNanos()進行等待。
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();
}
}
// 如果“當前generation已經損壞”,則拋出異常。
if (g.broken)
throw new BrokenBarrierException();
// 如果“generation已經換代”,則返回index。
if (g != generation)
return index;
// 如果是“超時等待”,並且時間已到,則通過breakBarrier()終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程。
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
// 釋放“獨佔鎖(lock)”
lock.unlock();
}
}
- nextGeneration
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}