notify必须在synchronized块内调用且锁对象一致,它不释放锁仅唤醒线程;需与wait配合用while循环检查条件,notify适用于等价等待者,notifyall更安全通用。

notify 方法本身不管理锁,它只负责唤醒等待线程;真正协调并发的是它与 synchronized 锁的配合机制。关键在于:notify 必须在已持有目标对象锁的同步块中调用,且唤醒动作与锁释放严格分离。
notify 必须在 synchronized 块内执行
这是硬性要求,否则会抛出 IllegalMonitorStateException。JVM 会检查当前线程是否正持有该对象的监视器锁。不是语法限制,而是语义需要——确保“条件判断 → 通知”这一逻辑整体受锁保护。
- 正确写法:
synchronized (lock) { if (condition) lock.notify(); } - 错误写法:
synchronized (objA) { objB.notify(); }(锁对象和 notify 对象不一致) - 典型陷阱:用
this同步,却对内部字段(如queue)调用notify(),导致唤醒失效
notify 不释放锁,只改变线程状态
调用 notify() 后,被唤醒的线程并不会立即执行。它只是从对象的等待队列移出,进入“就绪”状态,仍需等待当前 synchronized 块执行完毕、锁被释放后,才能参与锁竞争。
- 当前线程继续执行同步块内剩余代码,直到退出 synchronized 范围才释放锁
- 被唤醒线程在锁释放后重新竞争,获取锁成功才会继续运行
- 因此 notify 是“发信号”,不是“交控制权”
与 wait 配合形成条件等待闭环
notify 的价值体现在和 wait 的协作中:wait 主动释放锁并挂起,notify 在条件满足时唤醒等待者,二者共用同一把锁,保证状态检查与等待/通知的原子性。
- 避免通知丢失:加锁后执行
while (!ready) wait();,生产者修改 ready 并 notify,消费者不会因“检查完条件→还没 wait”之间被插队而错过信号 - 防止虚假唤醒:被 notify 唤醒的线程必须再次检查条件(用 while,不用 if),因为可能没收到通知就被唤醒
- 典型模式:
synchronized(lock) { ready = true; lock.notify(); }
notify 和 notifyAll 的选择影响并发行为
notify 唤醒单个线程,notifyAll 唤醒全部。选择取决于等待者是否等价:
- 用 notify:多个线程等待同一条件,唤醒任意一个即可推进(如线程池取任务)
- 用 notifyAll:等待者关心不同子条件,或无法预判哪个线程能处理(如生产者-消费者模型中有多个生产者和消费者)
- 多数业务场景推荐 notifyAll——避免线程饥饿,现代 JVM 性能开销可忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











