併發程式設計(5)——AQS之CountDownLatch、Semaphore、CyclicBarrier
CountDownLatch
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
通常情況下,countDown如下呼叫
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.countDown();
countDownLatch.await();
看一下countDown方法:
public void countDown() { sync.releaseShared(1); }
AQS中releaseShared方法如下:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
CountDownLatch中tryReleaseShared方法如下:
// 方法判斷許可如果減1之後是否為0,如果為0的話就執行doReleaseShared()方法。 protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } }
來看doReleaseShared()方法:
private void doReleaseShared() { /* * Ensure that a release propagates, even if there are other * in-progress acquires/releases. This proceeds in the usual * way of trying to unparkSuccessor of head if it needs * signal. But if it does not, status is set to PROPAGATE to * ensure that upon release, propagation continues. * Additionally, we must loop in case a new node is added * while we are doing this. Also, unlike other uses of * unparkSuccessor, we need to know if CAS to reset status * fails, if so rechecking. */ for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
不過尷尬的是,CountDownLatch這裡未做任何事情。
再看一下await()方法:
await方法會讓當前執行緒進入wait狀態,除非滿足下面兩個條件:
- count到0
- 執行緒中斷
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared方法如下:
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
所以,當state不是0的時候進入doAcquireSharedInterruptibly方法。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// 只有當state為0時r為1
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 如果state不為0,該執行緒會進入wait狀態
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
CountDownLatch文件中有一句非常重要的話:
Memory consistency effects: Until the count reaches zero, actions in a thread prior to calling countDown()
happen-before actions following a successful return from a corresponding await()
in another thread
大意是一個執行緒countdown()之前的操作happens-before另一個執行緒中await()之後的操作。
Semaphore
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
Semaphore主要用來限制獲取資源的執行緒數。
Actions in a thread prior to calling a "release" method such as release() happen-before actions following a successful "acquire" method such as acquire() in another thread
記憶體語義:release() happen-before acquire()之前
啟一個springboot專案,寫一個方法:
@RequestMapping("/test/semaphore")
@ResponseBody
public void test() throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 7; i++) {
int finalI = i;
new Thread(()->{
try {
semaphore.acquire();
System.err.println(Thread.currentThread() + "獲取了許可" + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "執行緒" + i).start();
}
new Thread(()->{
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(Thread.currentThread() + "要釋放許可" + semaphore.availablePermits());
semaphore.release();
}, "執行緒7").start();
}
一次輸出如下:
Thread[執行緒1,5,main]獲取了許可4
Thread[執行緒0,5,main]獲取了許可3
Thread[執行緒3,5,main]獲取了許可2
Thread[執行緒4,5,main]獲取了許可0
Thread[執行緒2,5,main]獲取了許可0
Thread[執行緒7,5,main]要釋放許可0
Thread[執行緒5,5,main]獲取了許可0
會發現,執行緒5獲取許可之前是先等執行緒7釋放許可。
至於執行緒6會因為由於許可為0,進入等待狀態。直到有執行緒釋放許可,來呼叫unparkSuccessor。
CyclicBarrier
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
Actions in a thread prior to calling await() happen-before actions that are part of the barrier action, which in turn happen-before actions following a successful return from the corresponding await() in other threads.
內部類Generation只有一個屬性broken(預設false)
我們發現,await()方法如下:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
進入dowait方法:
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();
}
// 來一個執行緒count減1,如果index為0,就會翻車
int index = --count;
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();
}
}
// 沒翻車(broken,interrupted,timed out)的話就執行下面的邏輯
// 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)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
下面進入trip.await()方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 往等待佇列加入節點Node
Node node = addConditionWaiter();
// 這裡釋放AQS中的state, 如果釋放失敗,會將node的waitstatus置為CANCELLED,這是傳參node的唯一用處
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果node有next就肯定返回true
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 如果當前執行緒
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
進入addConditionWaiter()
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
假如5個執行緒按順序進入await(),則此時,trip這個ConditionObject上firstWaiter==lastWaiter==new Node("執行緒0對應的執行緒", Node.CONDITION)
同時,因為dowait方法中的lock.lock(),AQS的同步佇列如下:
head節點--》執行緒1--》執行緒2--》執行緒3--》執行緒4(tail)
等待佇列: t0
當釋放執行緒0的鎖之後,喚醒執行緒1,將執行緒1加入等待佇列,執行緒2/3也加入等待佇列。此時同步佇列還剩下執行緒4。此時佇列情況是:
同步佇列:head節點
等待佇列:t0->t1->t2->t3
到了最後一個執行緒4執行的時候,index==0,執行nextGeneration,會signalAll trip這個Condition上的所有等待執行緒。所以經過signalAll之後,佇列情況變成了:
同步佇列:head->t0->t1->t2->t3
等待佇列:空
此時執行緒4執行,釋放鎖之後喚醒同步佇列上的第一個節