CountdownLatch 源码

构造方法

java 复制代码
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

Sync

java 复制代码
private static final class Sync extends AbstractQueuedSynchronizer {
   private static final long serialVersionUID = 4982264938641297070L;

   Sync(int count) {
       setState(count);
   }

   int getCount() {
       return getState();
   }

   // await调用:共享模式尝试获取锁
   protected int tryAcquireShared(int acquires) {
       // state == 0 返回1(获取成功),否则-1(阻塞)
       return (getState() == 0) ? 1 : -1;
   }

   // countDown调用:共享模式释放锁,state自减1
   protected boolean tryReleaseShared(int releases) {
       // CAS循环自旋减state
       for (;;) {
           int c = getState();
           if (c == 0)
               return false;
           int nextc = c - 1;
           if (compareAndSetState(c, nextc))
               return nextc == 0;
       }
   }
}

countdown

java 复制代码
public void countDown() {
    sync.releaseShared(1);
}

releaseShared

java 复制代码
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

tryReleaseShared

java 复制代码
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c - 1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

doReleaseShared

  • 遍历 AQS 阻塞队列,唤醒头部等待节点;
  • 等待线程被 unpark 唤醒,再次执行tryAcquireShared;
  • 如果 state=0,返回 1,获取共享锁成功,退出阻塞逻辑,主线程继续执行后续代码
java 复制代码
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 头节点标记SIGNAL,说明后继需要唤醒
            if (ws == Node.SIGNAL) {
                // CAS重置头节点状态为0,防止重复唤醒
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                // 唤醒头节点的后继等待线程(主线程)
                unparkSuccessor(h);
            }
            // 无等待线程,设置PROPAGATE传播标记
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        // 循环终止条件:中途head发生变化,说明有新节点被唤醒,继续循环传播
        if (h == head)
            break;
    }
}

await

java 复制代码
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

acquireSharedInterruptibly

java 复制代码
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

tryAcquireShared

java 复制代码
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

doAcquireSharedInterruptibly

  • 把当前主线程封装成 共享 SHARED 类型 Node
  • CAS 把节点加入 AQS 阻塞双向链表尾部
  • 循环自旋:
    1. 如果当前节点是队列头节点,再次调用 tryAcquireShared
    2. 执行 parkAndCheckInterrupt(),调用 LockSupport.park()
    3. 当前线程挂起,卡住,不再往下执行,等待其他线程 countDown 把 state 降到 0 唤醒
java 复制代码
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    // 1. 创建共享SHARED类型节点,加入队尾
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            // 获取当前节点前驱
            final Node p = node.prev;
            // 如果前驱是头结点,有资格尝试获取共享锁
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    // 获取共享锁成功,设置新head,传播唤醒
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            // 调整节点waitStatus,无资格则park阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

setHeadAndPropagate

java 复制代码
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // 保存旧头
    setHead(node); // 当前等待节点晋升为新head,清空thread、prev
    // propagate>0 说明还有剩余共享许可,需要继续唤醒后继
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        // 如果下一个节点也是共享节点,继续唤醒传播
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

getCount

java 复制代码
public long getCount() {
    return sync.getCount();
}

总结

await

plaintext 复制代码
latch.await()
→ sync.acquireSharedInterruptibly(1)
    → tryAcquireShared 返回 -1
    → doAcquireSharedInterruptibly(1)
        addWaiter(SHARED) 入队
        for 自旋
            tryAcquireShared 失败
            shouldParkAfterFailedAcquire 设置前驱 SIGNAL
            parkAndCheckInterrupt() 阻塞主线程

countdown

plaintext 复制代码
latch.countDown()
→ sync.releaseShared(1)
    → tryReleaseShared CAS state到0,返回true
    → doReleaseShared()
        CAS清空head SIGNAL标记
        unparkSuccessor 唤醒主线程
等待线程被唤醒,回到 doAcquireSharedInterruptibly 自旋
    tryAcquireShared 返回 1
    setHeadAndPropagate 晋升新 head、传播唤醒
    退出阻塞,await执行完毕
相关推荐
程序猿乐锅16 分钟前
一文讲透缓存穿透、击穿和雪崩
java·redis·spring·缓存·mybatis
xcl092530 分钟前
西安同城货运系统源码开发实战 核心功能实现与部署全指南
java·spring boot
pippocao36 分钟前
王者荣耀日志组件BqLog为什么这么快之1——高性能实时压缩日志
java
AC赳赳老秦40 分钟前
公开 CSV 数据集批量处理实战:用 OpenClaw 高效完成下载、清洗与标准化分析样本生成
java·开发语言·汇编·c++·python·deepseek·openclaw
ym hyd 1111 小时前
慢性病精细化管理平台源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
程序猿乐锅1 小时前
【黑马点评 | 第七篇】Redis 分布式锁的两种实现
java·数据库·redis·分布式·spring·缓存
SL_staff1 小时前
JVS私有化交付为何敢承诺100%源码开放与无兜底风险?
java·低代码·全栈
需要8261 小时前
MySQL MVCC 与事务隔离级别:从一条 update 看版本链
java·数据库·spring boot·mysql·spring cloud
Memory_荒年1 小时前
订单超时未支付?从“定时扫库”一步步到最终形态
java·后端
hai_android2 小时前
LruCache 图片浏览器内存缓存
android·java·kotlin