源码详解CountDownLatch
CountDownLatch,是一种常见同步器。其实现依赖于AQS(可以参考抽象队列式同步器AQS详解)
具体来说一个经典得应用案例是,主线程等待子线程执行完毕,再进行信息汇总,退出主函数。
如下代码所示。我们可以大胆猜测其初始化构造,赋值计数器值,之后,每次调用countDown
函数,计数器减一,当为零时,会唤醒调用await
函数阻塞得线程。下面从其源码角度进行验证。
import java.util.concurrent.CountDownLatch;
public class Main{
//初始化构造,赋值计数器值
public static CountDownLatch cdl = new CountDownLatch(2);
public static void main(String args[]) throws Exception {
Thread a = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Thread a working");
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
cdl.countDown();
}
});
Thread b = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Thread b working");
try{
Thread.sleep(1000);
}catch(Exception e) {
e.printStackTrace();
}
cdl.countDown();
}
});
a.start(); b.start();
cdl.await();
System.out.println("Main thread completed");
}
}
源码分析
-
CountDownLatch的构造函数
CountDownLatch构造函数,会调用其成员变量sync(AQS类型)得构造函数,Sync的构造函数,将state置为初始值
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } Sync(int count) { setState(count); }
-
countDown方法
countDown方法,会调用sync的
releaseShared
方法,最终会调用其父类AQS
的,releaseShared
方法,释放共享变量。releaseShared
会调用tryReleaseShared
释放资源,并且调用doReleaseShared
唤醒阻塞于AQS阻塞队列的线程。public void countDown() { sync.releaseShared(1); }
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }
tryReleaseShared
方法,释放资源,将state值减一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;w } }
-
await方法
调用
await
方法,最终会调用sync父类AQS的acquireSharedInterruptibly
方法,支持可中断的获取方法。acquireSharedInterruptibly
方法,包括两个过程,即tryAcquireShared
,请求资源,成功则立刻返回,否则doAcquireSharedInterruptibly
,进入阻塞队列等待唤醒。public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
acquireSharedInterruptibly
方法,支持可中断的获取方法public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
tryAcquireShared
函数尝试获取资源,如果state值为0,则代表计数器清零,请求成功,返回1,否则返回-1protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
获取失败,则
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) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
总结
至此,已完成原理的验证,我们再次总结一下CountDownLatch的使用过程
CountDownLatch同步器的使用过程总结如下
- 初始化时,指定计数器的值。
CountDownLatch(int count)
- 计数器值不为零时,调用
await
方法的线程,将会进入AQS阻塞队列,进行阻塞等待。 - 每当有线程调用
countDown
方法时,会将计数器减一,同时尝试唤醒调用await
方法的线程。 - 一旦计数器的值减为0,则调用
await
方法的线程,将被唤醒,从阻塞点,继续执行。
·