- CountDownLatch實現是基於CAS; 在jdk源碼中CountDownLatch內部實現了一個Sync繼承自AbstractQueuedSynchronizer,調用await和countDown分別是使用了sync的方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
- CyclicBarrier實現是基於ReentrantLock; CyclicBarrier內部定義了一個ReentrantLock與Condition
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;
nextGeneration(); // 所有柵欄都完成後重新創建一個Generation並且喚醒其它線程
return 0;
} finally {
if (!ranAction) // 什麼時候ranAction會爲false? 答:當command.run()報錯時
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 {
// 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();
if (g != generation) // 這裏說明上一次柵欄已經運行完了可以直接返回index
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
CyclicBarrier與CountDownLatch的作用類似,那爲什麼CyclicBarrier爲什麼不使用CAS來實現而是使用Lock來實現呢?
- ReentrantLock內部使用AQS來實現的鎖,而AQS本身也是使用了CAS所以從這個角度來講CyclicBarrier這使用了CAS;
- CyclicBarrier的一個特性就是可以重複使用,因此內部有一個count字段來記錄次數,如果不使用lock則在多線程中就會出現count不一致的問題;
爲什麼CyclicBarrier要定義一個Generation的靜態內部類,而且Generation只有一個broken屬性,爲什麼不直接定義一個broken成員變量?
- 還是因爲CyclicBarrier的重複使用特性,如果只定義一個broken成員變量當柵欄可以繼續但上一次線程沒有運行完成將無法正確判斷;