You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《Effective Java》条目72中CountDownLatch的正确实现方式咨询

修正《Effective Java》条目72中的CountDownLatch错误实现

你说得没错,条目72里的SlowCountDownLatch完全是个反面教材——它的await()方法靠无限自旋忙等,纯纯浪费CPU资源,根本不是正确的并发同步方式。我们确实需要用wait()和notifyAll()来替代这种傻等的循环,让线程在条件不满足时进入休眠,避免空耗资源。

先拆解下错误代码的核心问题:

你贴的错误实现里,await()的问题最致命:

public void await() {
    while (true) {
        synchronized (this) {
            if (count == 0) return;
        }
    }
}

这个方法会反复抢锁、检查count、释放锁,一直循环到count归0——这就是典型的忙等(busy waiting),在高并发场景下会把CPU拖垮,完全不可取。

正确的实现版本

下面是符合并发规范的修正版,用wait()/notifyAll()实现线程的等待与唤醒,完美解决忙等问题:

public class CorrectCountDownLatch {
    private int count;

    public CorrectCountDownLatch(int count) {
        if (count < 0) {
            throw new IllegalArgumentException(count + " < 0");
        }
        this.count = count;
    }

    public void await() throws InterruptedException {
        synchronized (this) {
            // 必须用while循环检查条件,防范虚假唤醒
            while (count > 0) {
                this.wait();
            }
        }
    }

    public synchronized void countDown() {
        if (count == 0) {
            return; // 已经归0,无需重复操作
        }
        count--;
        if (count == 0) {
            // 当count减到0时,唤醒所有等待的线程
            this.notifyAll();
        }
    }
}

关键细节解释

  • 为什么用while而不是if?:这是为了处理虚假唤醒(spurious wakeup)——线程可能在没有收到唤醒信号的情况下被系统唤醒,所以每次被唤醒后必须重新检查count是否真的为0,确保条件满足后再继续。
  • wait()的作用:调用wait()会让当前线程释放持有的锁,进入等待队列休眠,直到被其他线程唤醒,彻底避免了CPU空转。
  • notifyAll()的时机:只有当count最终减到0时才调用notifyAll(),这样所有等待在这个锁上的线程都会被唤醒,检查条件后继续执行。
  • 中断处理:await()声明了InterruptedException,因为wait()会响应线程中断,这是并发编程的规范做法,让调用者可以合理处理中断场景。

当然,实际开发中直接用JDK自带的java.util.concurrent.CountDownLatch就好,这个实现只是帮你理解其底层的同步逻辑~

内容的提问来源于stack exchange,提问作者myoldgrandpa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:18:52