源码详解CountDownLatch

源码详解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,否则返回-1

protected 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方法的线程,将被唤醒,从阻塞点,继续执行。

·

作者:zycxnanwang

相关推荐

Django cookie 与 session

Django cookie 与 session

浅谈ASP.NET Core 中jwt授权认证的流程原理

IdentityServer4 QuckStart 授权与自定义Claims的问题

TypeScript的安装、使用、自动编译的实现