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()的线程
相关推荐
CHHH_HHH44 分钟前
【Linux网络篇】初识网络与Socket编程基础
linux·服务器·c语言·开发语言·网络·后端·1024程序员节
弦儿1 小时前
后端 Web 实战:员工管理(新增员工 + 事务管理 + 文件上传)
java·后端
Gopher_HBo1 小时前
Go reflect包值操作 Value 的读写、可设置性与 interface 布局
后端
excel1 小时前
用 Node.js 搭建一套可持续获客的 SEO 内容系统:前端工程师 30 天实战方案
前端·后端
阿良别做梦1 小时前
LangGraph 学习之路:给文档摘要接上模型
后端·开源
Random Glow1 小时前
一条 SQL 从输入到返回,中间经历了什么
数据库·后端·mysql·性能优化·innodb·1024程序员节
GISer_Jing3 小时前
营销活动低代码前后台方案
后端
IT_陈寒3 小时前
React项目实战经验分享:如何避免useEffect闭包陷阱导致的陈旧状态问题?
前端·人工智能·后端
Maynor9963 小时前
免费 Cloudflare 只有 10 个 D1?满了之后的「穷鬼打法」:表前缀复用同一个库
数据库·后端·云计算·cloudflare
沙蒿同学12 小时前
不引 Ghostscript:我用纯 Go 给 PDF 做了一场「压缩手术」
前端·后端·github