CountDownLatch源码分析

基于AQS实现,有关AQS的原理参考juejin.cn/post/748158... ,本文涉及AQS的方法不再重复分析。

构造方法

arduino 复制代码
public CountDownLatch(int count) {
    //参数值校验
    if (count < 0) throw new IllegalArgumentException("count < 0");
    //创建Sync类,继承AQS
    this.sync = new Sync(count);
}
scss 复制代码
Sync(int count) {
    //设置state值
    setState(count);
}

await()

csharp 复制代码
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
java 复制代码
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
     //中断状态,抛出中断异常   
    if (Thread.interrupted())
        throw new InterruptedException();
    // state等于0说明所有的线程都已经countDown了 
    if (tryAcquireShared(arg) < 0)
        //加入同步队列并阻塞
        doAcquireSharedInterruptibly(arg);
}
arduino 复制代码
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
java 复制代码
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    //构建共享模式节点添加到同步队列
    //这里同步队列里就只是一个哨兵节点和一个执行await()线程节点
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            //前驱节点
            final Node p = node.predecessor();
            //是头节点
            if (p == head) {
                //看state是否等于0
                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);
    }
}

await()逻辑总结:

  1. 先尝试查看state是不是0,是0说明所有的线程都已经countDown了;
  2. 不是0,说明还有线程没有走完业务逻辑执行countDown操作,把执行await()方法的线程加入到同步队列;
  3. 前驱节点是头节点的话先尝试在看state是否是0,不是的话阻塞线程,等countDown后为0唤醒

countDown()

csharp 复制代码
public void countDown() {
    sync.releaseShared(1);
}
arduino 复制代码
public final boolean releaseShared(int arg) {
    //判断state是否是正常countDown操作减为0
    if (tryReleaseShared(arg)) {
        //唤醒同步队列节点,也就是执行await()的线程
        doReleaseShared();
        return true;
    }
    return false;
}
ini 复制代码
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        //状态减1    
        int nextc = c-1;
        //CAS设置
        if (compareAndSetState(c, nextc))
            //为0成功
            return nextc == 0;
    }
}

countDown()逻辑总结:

  1. 循环减1state直到为0,然后唤醒执行await()的线程
相关推荐
thinktik3 分钟前
AWS EKS安装S3 CSI插件[AWS 海外区]
后端·kubernetes·aws
Tony Bai2 小时前
【Go 网络编程全解】12 本地高速公路:Unix 域套接字与网络设备信息
开发语言·网络·后端·golang·unix
Yeats_Liao4 小时前
Go Web 编程快速入门 06 - 响应 ResponseWriter:状态码与头部
开发语言·后端·golang
mit6.8244 小时前
[Agent可视化] 编排工作流(Go) | Temporal引擎 | DAG调度器 | ReAct模式实现
开发语言·后端·golang
猪哥-嵌入式5 小时前
Go语言实战教学:从一个混合定时任务调度器(Crontab)深入理解Go的并发、接口与工程哲学
开发语言·后端·golang
thinktik5 小时前
AWS EKS 计算资源自动扩缩之Fargate[AWS 海外区]
后端·kubernetes·aws
不爱编程的小九九6 小时前
小九源码-springboot099-基于Springboot的本科实践教学管理系统
java·spring boot·后端
lang201509286 小时前
Spring Boot集成Spring Integration全解析
spring boot·后端·spring
雨夜之寂6 小时前
第一章-第二节-Cursor IDE与MCP集成.md
java·后端·架构
大G的笔记本6 小时前
Spring IOC和AOP
java·后端·spring