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执行完毕
相关推荐
深盾科技_Virbox2 小时前
软件交付保护如何从加壳走向代码虚拟化
java·python·安全
学计算机的计算基2 小时前
二叉树算法下篇:递归核心技巧与高频面试题详解
java·笔记·算法
Liquad Li2 小时前
Salesforce高级开发面试题
java·网络·面试·crm·salesforce
Zhou1411362 小时前
Java常见面试题7
java·开发语言
带刺的坐椅2 小时前
代码审查 Agent Harness 实战:AI 自动 Code Review
java·ai·llm·agent·solon·soloncode·harness
可儿·四系桜2 小时前
经纬度转北斗网格位置码(非极地区域,编码篇)
java·物联网
唐青枫2 小时前
Java ActiveMQ 实战指南:从 JMS 队列、主题到 Spring Boot 消息处理
java
码兄科技10 小时前
Java AI智能体开发实战:从零构建智能对话系统指南
java·开发语言·人工智能