拓冰建站拓冰建站
首页 / 资讯中心 / 正文

wait() 为什么要写在 while 循环里?写在 if里不行吗?

为什么wait()必须写在while循环里不能用if1. 防止虚假唤醒Spurious Wakeup线程从wait()返回并不一定是因为被notify/notifyAll通知了操作系统层面可能在线程未收到任何通知的情况下将其唤醒即虚假唤醒。如果使用的是if线程被虚假唤醒后会直接执行条件判断后面的代码而此时条件可能仍然不满足。2. 被notifyAll()唤醒的线程需要重新竞争条件即使没有虚假唤醒使用notifyAll()时会唤醒所有等待线程它们会逐个获取锁并返回。例如经典的生产者-消费者场景多个消费者线程因队列为空而进入等待一个生产者放入一个元素后调用notifyAll()所有消费者线程都被唤醒但只有一个消费者能取到数据用if的话其他消费者取不到数据也会直接往下执行产生错误逻辑标准写法对比❌ 错误写法——用ifsynchronized(obj){if(条件不满足){// 只用 if 判断一次obj.wait();}// 线程被唤醒后直接执行这里 → 条件可能仍不满足对应的逻辑;}✅ 正确写法——用whilesynchronized(obj){while(条件不满足){// 被唤醒后重新检查条件obj.wait();}对应的逻辑;// 条件确实满足时才执行}官方规范说明在Object#wait的规范中明确指出等待方应遵循以下原则获取对象的锁。如果条件不满足那么调用对象的wait()方法被通知后仍要检查条件。条件满足则执行对应的逻辑。这正是while循环要做的事——每次从wait()返回后都重新检查条件而不是只检查一次。真实源码示例在 JDK 的ArrayBlockingQueue#put()方法中也是用while来防止虚假唤醒的publicvoidput(Ee)throwsInterruptedException{checkNotNull(e);finalReentrantLocklockthis.lock;lock.lockInterruptibly();try{// 设计注意点用while不用if是为了防止虚假唤醒while(countitems.length)notFull.await();// 队列满了生产者等待enqueue(e);}finally{lock.unlock();}}总结对比ifwhile条件检查次数进入wait前检查一次每次从wait返回后都重新检查虚假唤醒直接执行后续逻辑可能出问题重新判断条件安全notifyAll()场景被错误唤醒的线程会越界操作条件不满足则继续等待结论不可行必须使用简单说使用while的目的是确保线程从wait()返回时等待条件确实已经满足这是并发编程中的标准实践也是《Java 并发编程的艺术》和 JDK 源码中推荐的写法。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门