1. 首先看一下CountDownLatch的構造函數
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
然後看一下Sync類,是一個靜態內部類,繼承了AQS,重寫了tryAcquireShared()
與tryReleaseShared()
兩個共享的方法
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
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;
}
}
}
構造對象時,傳入state初始值
2. 看一下countDown()方法
public void countDown() {
sync.releaseShared(1);
}
countDown時會把state-1,看一下releaseShared()方法的實現
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared()
爲內部類實現,如果state = state-1設置成功,並且state爲0時返回true,爲true時調用了doReleaseShared()
方法。介紹完await()方法再介紹此方法
3. 看一下await()方法
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);
}
await方法中獲取1個狀態,最終調用內部類實現的tryAcquireShared()
方法,此方法只有在state==0時才返回1,否則返回-1(返回-1表示state>0,也就是計數沒有用完),調用doAcquireSharedInterruptibly()
方法,看一下此方法
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
addWaiter()
方法表示增加一個新Node節點到等待隊列尾部,並返回這個新節點。當執行到tryAcquireShared()
方法時,若state!=0,返回-1,後面會進入parkAndCheckInterrupt()
方法等待
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
4. 下面看一下doReleaseShared()方法
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
看一下unparkSuccessor()
方法
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
node.compareAndSetWaitStatus(ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
LockSupport.unpark(s.thread);
}
unparkSuccessor()
方法會喚醒等待的線程,正好喚醒上面await()
方法park的線程
5. 總結
利用AQS的state表示計數值,每次調用countDown()方法時會減一,當減到0時會喚醒await()方法等待的線程。
實時內容請關注微信公衆號,公衆號與博客同時更新:程序員星星