wait() 和 notify() 为什么必须在 synchronized 代码块中使用
wait() 和 notify() 必须在 synchronized 代码块中使用,这是 Java 多线程设计中的一个强制规定,其原因涉及到 Java 对对象监视器(monitor)的使用机制。
1. 背景知识:对象监视器是什么?
Java 中的每一个对象都有一个监视器锁(monitor),也叫“内置锁”或“对象锁”。这个锁可以被 synchronized 获取。
wait()会让当前线程释放监视器锁,并进入等待队列。notify()会让某个等待队列中的线程重新获得锁的竞争权。
2. 为什么必须在 synchronized 代码块中?
原因一:wait() 和 notify() 依赖对象的监视器
wait()、notify()、notifyAll()方法是Object类的方法,而不是Thread。- 它们必须在当前线程已经持有该对象的监视器锁时调用(也就是在
synchronized(obj)代码块中),否则抛出IllegalMonitorStateException异常。
原因二:保证线程安全和状态一致性
- 如果在没有同步的情况下调用
wait()和notify(),可能会导致: - 线程状态混乱(比如一个线程刚进入等待,但还没加入等待队列,另一个线程就调用了
notify(),会错过通知)。 - 数据竞态(多个线程访问共享数据而没有同步保护)。
3. 强制规定的体现
Object lock = new Object();
lock.wait(); // ❌ 编译通过,运行时抛 IllegalMonitorStateException
必须这样写才合法:
synchronized (lock) {
lock.wait(); // ✅ 合法,线程当前拥有lock对象的监视器
}
4. 举个完整例子
class WaitNotifyExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程A等待...");
lock.wait();
System.out.println("线程A收到通知");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
Thread notifyingThread = new Thread(() -> {
synchronized (lock) {
System.out.println("线程B通知...");
lock.notify();
}
});
waitingThread.start();
try { Thread.sleep(100); } catch (InterruptedException e) {}
notifyingThread.start();
}
}
5. 总结:为什么要在 synchronized 中调用?
| 方法 | 是否必须在 synchronized 中 | 原因 |
|---|---|---|
wait() | 是 | 释放锁并进入等待,需要拥有对象锁 |
notify() | 是 | 通知等待线程,需要拥有对象锁 |
notifyAll() | 是 | 同上,通知所有等待该对象锁的线程 |
6. 官方说明与资料
- 🔗 Java 官方 API 文档:Object.wait()
- 🔗 JLS (Java Language Specification) on Synchronization
- 📘 《Java 并发编程实战》第 14 章:构建自定义同步工具
总结口诀
对象监视器,锁内才可操;wait 释放锁,notify 唤人跑。
更多详细内容请关注其他相关文章!