必须在synchronized块中调用wait/notify,且锁对象与wait/notify对象必须一致;否则抛IllegalMonitorStateException,因其需保证“检查条件→等待”原子性、防止通知丢失、确保锁释放与重入机制正确。

必须在 synchronized 块里调用,且锁对象和 wait/notify 对象必须是同一个——否则直接抛 IllegalMonitorStateException。
为什么非得套 synchronized?
不是语法限制,而是语义刚需:防止“检查条件 → 等待”之间发生竞态。比如消费者发现 queue.isEmpty() 为 true,刚想 wait(),生产者就插入一个元素并 notify() 了——这个通知会丢失,因为消费者还没进等待队列。加锁后,整个“检查 + 等待”变成原子操作;同时 wait() 会自动释放锁,让生产者能进来操作队列并 notify()。
-
wait()的三件事:释放当前持有的锁 → 进入对象的等待队列 → 被唤醒后重新竞争该锁(不保证立刻获得) -
notify()不释放锁,它只唤醒一个等待线程;真正释放锁要等当前synchronized块执行完 - 没有锁,
notify()就像往空房间喊话——没人听见,也无人响应
wait() 和 notify() 必须用同一个对象调用
常见错误是:在 synchronized(obj1) 里调 obj2.wait(),或反过来。JVM 会校验当前线程是否持有该对象的监视器锁,不持有时一律报错。
- 正确写法:
synchronized (lock) { while (!conditionMet) { lock.wait(); } } - 错误写法:
synchronized (objA) { objB.wait(); // 报 IllegalMonitorStateException } - 典型陷阱:用
this同步,却对某个字段对象(如queue)调wait();或者用new Object()创建多个锁对象,导致notify()唤醒不了真正的等待者
为什么推荐用 while 而不是 if 检查条件?
wait() 可能被虚假唤醒(spurious wakeup),即没收到 notify() 也被系统唤醒。用 if 会导致线程醒来后直接往下走,而条件其实仍不满足,引发逻辑错误。
立即学习“Java免费学习笔记(深入)”;
- 正确模式:
while (!hasData()) { dataLock.wait(); } - 错误模式:
if (!hasData()) { dataLock.wait(); // 醒来后不再检查,可能读到 null 或越界 } - 所有基于
wait/notify的场景(生产者-消费者、线程协调、资源池等待)都应遵守“while + 条件变量 + 锁对象一致”铁三角
最容易被忽略的一点:唤醒不等于立即执行。被 notify() 的线程只是从等待队列移到锁竞争队列,它要和其他线程(包括新来的)一起抢锁;抢不到,就继续阻塞——所以业务逻辑不能假设“一唤醒就能干活”。










