notify()随机唤醒一个等待线程,该线程需重新竞争锁才能继续执行;notifyall()唤醒所有等待线程,均需竞争锁,二者均须在synchronized块中调用且依赖while循环防虚假唤醒。

notify 方法本身不直接决定唤醒哪个线程,它只是从等待队列中随机选择一个线程“发出唤醒信号”,真正恢复执行依赖 JVM 调度和锁竞争。
notify 的作用机制
当一个线程在 synchronized 块中调用 notify(),JVM 会从该对象的等待队列(wait set)中选取一个处于 WAITING 状态的线程,将其状态改为 RUNNABLE,并将其移出等待队列。但该线程不会立刻执行——它必须重新竞争该对象的监视器锁(monitor lock),只有成功获取锁后,才能从 wait() 方法返回,继续向下执行。
- notify 不保证唤醒“最先 wait 的线程”,也不按优先级或顺序唤醒,行为由 JVM 实现决定(通常是随机或 FIFO,但不保证)
- 被 notify 的线程若未获得锁,会进入锁的入口队列(Entry Set),和其他试图进入 synchronized 块的线程一起竞争
- 如果当前没有线程在 wait,notify 不做任何事,也不会报错
典型通信场景:生产者-消费者模型
notify 常用于协调多个线程对共享资源的访问。例如,一个消费者线程 wait() 等待缓冲区非空;生产者放入数据后调用 notify(),提醒消费者可以取数据:
- 消费者在获取锁后检查条件(如 queue.isEmpty()),为真则 wait(),释放锁并进入等待队列
- 生产者获取同一把锁,添加元素后调用 notify(),唤醒一个等待中的消费者
- 被唤醒的消费者重新竞争锁,成功后从 wait() 返回,再次检查条件(防止虚假唤醒),再消费
notify 和 notifyAll 的关键区别
notify 只唤醒一个线程,notifyAll 唤醒所有等待线程。选择取决于业务逻辑:
- 若多个等待线程等待**不同条件**(如缓冲区空、满、特定类型数据),应使用 notifyAll,让所有线程重新判断各自条件
- 若所有等待线程等待**同一条件**(如“有新任务到来”),且只需一个线程响应,notify 更高效,避免不必要的竞争
- 用 notify 替代 notifyAll 时,必须确保唤醒任意一个线程都能正确推进逻辑,否则可能死锁或遗漏处理
必须配合 while 循环检查条件
不能用 if 判断后调用 wait(),而要用 while。因为存在虚假唤醒(spurious wakeup)或条件已变的情况:
- 即使没被 notify,wait() 也可能提前返回(JVM 允许)
- 多个线程被 notifyAll 唤醒后,只有第一个拿到锁的能处理,其余线程需重新检查条件是否仍满足
- 正确写法:while (!condition) { obj.wait(); },每次从 wait 返回都重新验证
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











