第一部分:wait/notify基础
理解wait/notify在多线程通信中的核心作用
学习目标
掌握wait(), notify(), notifyAll()方法的使用场景和基本用法
wait()
使当前线程进入等待状态,直到其他线程调用notify()或notifyAll()方法
特点: 释放对象锁,线程进入WAITING状态
语法: object.wait()
notify()
唤醒在此对象监视器上等待的单个线程
特点: 随机唤醒一个等待线程
语法: object.notify()
notifyAll()
唤醒在此对象监视器上等待的所有线程
特点: 唤醒所有等待线程
语法: object.notifyAll()
1. 获取对象锁
线程进入同步代码块,获取对象锁
2. 检查条件
检查是否满足执行条件
3. 条件不满足,调用wait()
释放锁,线程进入等待状态
4. 其他线程调用notify()
唤醒等待线程
5. 重新获取锁
被唤醒的线程重新尝试获取锁
6. 再次检查条件
检查条件是否满足,继续执行
关键注意事项
1. wait()和notify()必须在同步代码块内调用
2. 使用while循环检查条件,防止虚假唤醒
3. notify()随机唤醒一个线程,notifyAll()唤醒所有线程
4. 调用wait()后线程释放锁,进入WAITING状态
第二部分:wait/notify工作原理
深入理解wait/notify的底层机制
学习目标
掌握监视器锁、等待队列和状态转换机制

wait/notify基于对象监视器实现线程通信
对象监视器
每个Java对象都有一个监视器锁,包含:
- 锁状态(被哪个线程持有)
- 等待队列(调用wait()的线程)
- 入口队列(等待获取锁的线程)
状态转换
线程调用wait()后的状态变化:
- RUNNABLE → WAITING
- 释放对象锁
- 进入等待队列
- 被notify()唤醒后进入入口队列
- 重新获取锁后进入RUNNABLE
虚假唤醒
线程可能在没有调用notify()的情况下被唤醒
原因: 操作系统调度、JVM实现
解决方案: 使用while循环检查条件
第三部分:生产者-消费者模式
使用wait/notify实现经典的生产者-消费者模式
学习目标
掌握使用wait/notify实现线程间协作的经典模式
模式要点
1. 使用共享队列作为缓冲区
2. 生产者检查队列是否满,满则wait()
3. 消费者检查队列是否空,空则wait()
4. 生产后notifyAll()唤醒消费者
5. 消费后notifyAll()唤醒生产者
第四部分:常见错误与陷阱
避免wait/notify使用中的常见问题
1. 非同步块内调用
在非同步代码块中调用wait/notify
正确做法:必须在synchronized代码块内调用
2. 使用if检查条件
使用if而不是while检查条件
正确做法:使用while循环检查条件
3. 忘记notify
修改条件后忘记调用notify/notifyAll
正确做法:修改条件后调用notify/notifyAll
4. 错误使用notify
使用notify()但需要唤醒多个线程
正确做法:需要唤醒所有线程时使用notifyAll()
5. 忽略InterruptedException
忽略wait()抛出的InterruptedException
正确做法:正确处理中断异常
6. 锁对象不一致
使用不同对象作为锁和wait/notify对象
正确做法:使用同一个对象作为锁和wait/notify对象
第五部分:wait/notify vs Condition
比较两种线程通信机制的差异
wait/notify
Java内置的线程通信机制
优点: 简单易用,无需额外依赖
缺点: 功能有限,只有一个等待队列
Condition
Lock接口提供的更灵活的通信机制
优点: 多个等待队列,支持超时和中断
缺点: 使用稍复杂,需要手动释放锁
特性 | wait/notify | Condition |
---|---|---|
等待队列数量 | 1个 | 多个(可创建多个Condition) |
超时等待 | 不支持 | 支持(await(long, TimeUnit)) |
中断响应 | 支持(抛出InterruptedException) | 支持(awaitUninterruptibly()) |
锁机制 | synchronized内置锁 | Lock接口实现 |
使用复杂度 | 简单 | 稍复杂 |
适用场景 | 简单同步需求 | 复杂同步需求 |
选择建议
1. 简单场景使用wait/notify
2. 需要多个等待队列时使用Condition
3. 需要超时控制时使用Condition
4. 需要更细粒度控制时使用Condition
第六部分:wait/notify最佳实践
高效、安全地使用wait/notify机制
最佳实践
- 使用while循环检查条件
- 在同步块内调用wait/notify
- 修改条件后调用notify/notifyAll
- 使用private final对象作为锁
- 正确处理InterruptedException
避免做法
- 避免在非同步块内调用
- 避免使用if检查条件
- 避免忽略InterruptedException
- 避免使用public对象作为锁
- 避免不必要的notifyAll()调用
实践1:正确使用锁对象
- 使用private final对象作为锁
- 避免使用字符串常量或基本类型
- 避免使用可能被修改的对象
实践2:优化通知机制
- 优先使用notifyAll()
- 仅在确定只唤醒一个线程时使用notify()
- 在锁释放前调用notify/notifyAll
实践3:处理中断
- 正确处理InterruptedException
- 恢复中断状态
- 清理资源后退出
性能优化建议
1. 减少不必要的notifyAll()调用
2. 避免在循环中频繁调用wait/notify
3. 使用超时版本的wait(long timeout)
4. 合理设计条件检查逻辑
5. 避免长时间持有锁
第七部分:练习与测验
通过练习巩固wait/notify知识
动手练习
- 实现一个线程安全的阻塞队列
- 使用wait/notify实现多线程倒计时门闩
- 实现一个简单的线程池使用wait/notify
- 使用wait/notify实现读写锁
wait/notify知识测验
深入学习资源
1. 《Java并发编程实战》第14章
2. Oracle官方文档:Object.wait()/notify()
3. Java并发编程之美:wait/notify机制
4. OpenJDK源码:ObjectMonitor实现