《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
相关产品推荐
相关产品推荐

