1:写在前面
CountDownLatch是并发工具类,旨在当n个线程完成某一个任务,或达到某一种状态的时候,其他的线程才会开始工作,否则的话会一直处于阻塞状态
2:架构设计
CountDownLatch内部使用的是一个继承于AbstractQueuedSynchronizer(AQS)的Sync同步器,并初始化一个state计数器实现类一套共享方法
3:执行流程源码解析
3.1:构造方法 new CountDownLatch(int count)
创建一个CountDownLatch,调用new Sync(count) 将计数器的个数传给同步器
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
static final class Sync extends AbstractQueuedSynchronizer
Sync(int count) {
setState(count);
}
3.2:扣减函数 countDown()
内部调用的是AQS的 releaseShared()方法
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//使用CAS扣减,当count==0的时候返回true
if (tryReleaseShared(arg)) {
//唤醒阻塞线程的主要方法
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//CAS进行count值的设置
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
3.3:阻塞函数 await()
public void await() throws InterruptedException {
//还是调用同步器的方法
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquireShared(arg) = -1
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
//将当前任务放到队列中,并阻塞
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) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// parkAndCheckInterrupt 一定会执行,会调用 LockSupport.park(this)方法,将线程阻塞住,那么是什么时候,这个阻塞的线程被唤醒的呢??????
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3.4:唤醒被阻塞的线程
回到3.2中的 releaseShared 方法,doReleaseShared方法
public final boolean releaseShared(int arg) {
//使用CAS扣减,当count==0的时候返回true
if (tryReleaseShared(arg)) {
//唤醒阻塞线程的主要方法
doReleaseShared();
return true;
}
return false;
}
在其中有这么一行 unparkSuccessor 方法
private void doReleaseShared() {
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;
}
}
调用 LockSupport.unpark(s.thread); 来唤醒被阻塞的线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//唤醒被阻塞的线程
LockSupport.unpark(s.thread);
}
4:总结
- AQS是整个并发工具类的重要的组件,当实现的方法不同,用于的场景也不一样
- CountDownLatch 内部的同步器实现了AQS的共享组件(公平锁),AQS使用了大量的CAS(乐观锁的一种实现)自旋来获取锁