wait()和notify()是object类提供的协作式线程通信方法,必须在synchronized块中调用,需遵循锁持有、while循环判条件、notify/notifyall合理选择三原则。

在 Java 中,wait() 和 notify() 是 Object 类提供的方法,用于实现线程间的**协作式通信**,常用于生产者-消费者、任务等待-唤醒等场景。它们必须在 synchronized 同步块或同步方法中调用,否则会抛出 IllegalMonitorStateException。
核心规则必须遵守
使用前务必理解以下三点,否则极易出错:
-
只能在 synchronized 持有锁的对象上调用:比如
obj.wait()要求当前线程已获得obj的 monitor 锁(即在synchronized(obj) { ... }内); -
wait() 会释放锁并进入等待队列,直到被
notify()或notifyAll()唤醒,且被唤醒后需重新竞争锁才能继续执行; - 永远用 while 循环检查条件,而不是 if:防止虚假唤醒(spurious wakeup)或唤醒后条件已失效。
典型示例:生产者-消费者模型
以一个容量为 1 的共享缓冲区为例:
class Buffer {
private String data = null;
public void produce(String item) throws InterruptedException {
synchronized (this) {
while (data != null) { // 缓冲区满,等待消费
this.wait();
}
data = item;
System.out.println("Produced: " + item);
this.notify(); // 唤醒可能等待的消费者
}
}
public String consume() throws InterruptedException {
synchronized (this) {
while (data == null) { // 缓冲区空,等待生产
this.wait();
}
String item = data;
data = null;
System.out.println("Consumed: " + item);
this.notify(); // 唤醒可能等待的生产者
return item;
}
}
}
注意:notify() 只唤醒一个等待线程(不保证是哪一个是生产者还是消费者),若需确保所有相关线程都被考虑,应使用 notifyAll() —— 尤其在多个同类线程共存时更安全。
常见陷阱与避坑建议
-
不要在静态方法中直接用
this.wait():静态上下文没有实例this,应显式使用类锁,如synchronized(Buffer.class) { ... }; - 避免 notify() 后立即释放锁导致竞争漏洞:JVM 保证唤醒发生在锁释放前,但逻辑上仍建议唤醒后尽快退出同步块;
- wait(long timeout) 更可控:带超时的 wait 可防止无限等待,适用于需要兜底处理的场景(如网络响应等待);
-
优先考虑高阶工具:实际开发中推荐使用
java.util.concurrent包下的BlockingQueue、CountDownLatch、Condition等,它们更安全、语义更清晰、支持中断。
替代方案提醒:Condition 更灵活
如果要用显式锁(ReentrantLock),可搭配 Condition 实现类似功能,还能支持多个等待队列:
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
// 生产者中:
lock.lock();
try {
while (isFull()) notFull.await();
// 生产...
notEmpty.signal();
} finally {
lock.unlock();
}
这种方式比原始 wait/notify 更精准、可读性更好,也支持中断。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











