线程通信(wait、notify)与虚假唤醒
wait() 和 notify() 介绍
wait() 和 notify() 是 Java 中用于线程间通信的基本机制,定义在 Object 类中(所有 Java 对象的基类),必须配合 synchronized 使用。
wait()
- 让当前线程释放锁并进入等待状态(WAITING)
- 直到其他线程调用
notify()/notifyAll()或线程被中断 - 必须在同步代码块中调用(持有对象锁时)
notify()
- 随机唤醒一个在该对象上等待的线程
- 被唤醒的线程会尝试重新获取锁
- 必须在同步代码块中调用(持有对象锁时)
notifyAll()
- 唤醒所有在该对象上等待的线程
- 唤醒后的线程会竞争获取锁
- 必须在同步代码块中调用(持有对象锁时)
生产者-消费者示例
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerExample {
private final Queue<Integer> queue = new LinkedList<>();
private final int MAX_SIZE = 10;
private final Object lock = new Object();
// 生产者
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (lock) {
// 使用while而不是if防止虚假唤醒
while (queue.size() == MAX_SIZE) {
lock.wait();
}
queue.offer(value++);
System.out.println("Produced: " + value + ", Queue size: " + queue.size());
lock.notifyAll(); // 唤醒所有等待线程
Thread.sleep(500); // 模拟生产耗时
}
}
}
// 消费者
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
// 使用while而不是if防止虚假唤醒
while (queue.isEmpty()) {
lock.wait();
}
int value = queue.poll();
System.out.println("Consumed: " + value + ", Queue size: " + queue.size());
lock.notifyAll(); // 唤醒所有等待线程
Thread.sleep(1000); // 模拟消费耗时
}
}
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
Thread producer = new Thread(() -> {
try {
example.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
example.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
虚假唤醒(Spurious Wakeup)
什么是虚假唤醒?
- 线程在没有收到
notify()/notifyAll()或中断的情况下从wait()中醒来 - 虽然不常见,但必须防范
如何防范?
-
总是使用
while循环检查条件,而不是if语句synchronized (lock) { while (conditionNotMet) { // 不是 if (conditionNotMet) lock.wait(); } // 执行操作 } -
这样即使发生虚假唤醒,线程会再次检查条件并可能重新等待
底层原因:
- 与操作系统线程调度机制有关
- Java 规范明确允许这种行为(JLS 17.2.1)
- 设计上是为了在某些情况下提高性能

浙公网安备 33010602011771号