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()的线程
相关推荐
心在飞扬7 分钟前
ReRank重排序提升RAG系统效果
前端·后端
喝茶与编码7 分钟前
Python异步并发控制:asyncio.gather 与 Semaphore 协同设计解析
后端·python
不早睡不改名9 分钟前
网络编程基础:从BIO到NIO再到AIO(一)
后端
开源之眼10 分钟前
《github star 加星 Taimili.com 艾米莉 》为什么Java里面,Service 层不直接返回 Result 对象?
java·后端·github
心在飞扬12 分钟前
RAPTOR 递归文档树优化策略
前端·后端
zone773921 分钟前
003:RAG 入门-LangChain 读取图片数据
后端·python·面试
心在飞扬26 分钟前
LangChain Parent Document Retriever (父文档检索器)
后端
zone773934 分钟前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
用户83562907805135 分钟前
在 PowerPoint 中用 Python 添加和定制形状的完整教程
后端·python
武子康39 分钟前
大数据-240 离线数仓 - 广告业务 Hive ADS 实战:DataX 将 HDFS 分区表导出到 MySQL
大数据·后端·apache hive