wait
、notify
和 notifyAll
是 Java 中用于实现线程间通信和同步的关键方法,它们都是 Object
类的方法,必须在同步代码块或同步方法中使用(即需要持有对象的监视器锁)。它们的作用如下:
1. wait()
- 作用:使当前线程进入等待状态,并释放持有的对象锁。
- 行为:
- 调用
wait()
后,当前线程会释放锁,并进入WAITING
状态。 - 线程会一直等待,直到其他线程调用
notify()
或notifyAll()
唤醒它,或者被中断(InterruptedException
)。 - 被唤醒后,线程需要重新获取锁才能继续执行。
- 调用
- 使用场景:
- 用于线程间的条件等待,例如生产者-消费者模型。
示例:
synchronized (lock) {
while (conditionNotMet) {
lock.wait(); // 释放锁并等待
}
// 条件满足后继续执行
}
2. notify()
- 作用:随机唤醒一个正在等待该对象锁的线程。
- 行为:
- 调用
notify()
后,会从等待队列中随机选择一个线程唤醒。 - 被唤醒的线程需要重新获取锁才能继续执行。
- 如果没有线程在等待,则
notify()
不会有任何效果。
- 调用
- 使用场景:
- 用于唤醒单个等待线程,例如生产者-消费者模型中的生产者唤醒消费者。
示例:
synchronized (lock) {
// 修改条件
conditionMet = true;
lock.notify(); // 唤醒一个等待线程
}
3. notifyAll()
- 作用:唤醒所有正在等待该对象锁的线程。
- 行为:
- 调用
notifyAll()
后,会唤醒所有等待队列中的线程。 - 被唤醒的线程需要竞争获取锁,只有获取锁的线程才能继续执行。
- 如果没有线程在等待,则
notifyAll()
不会有任何效果。
- 调用
- 使用场景:
- 用于唤醒所有等待线程,例如多个消费者等待生产者生产数据。
示例:
synchronized (lock) {
// 修改条件
conditionMet = true;
lock.notifyAll(); // 唤醒所有等待线程
}
4. 关键点总结
方法 | 作用 | 释放锁 | 唤醒线程数量 | 使用场景 |
---|---|---|---|---|
wait() | 使当前线程等待并释放锁 | 是 | – | 线程条件等待 |
notify() | 唤醒一个等待线程 | 否 | 1 | 唤醒单个线程 |
notifyAll() | 唤醒所有等待线程 | 否 | 全部 | 唤醒所有线程 |
5. 注意事项
- 必须在同步代码块中使用:
wait()
、notify()
和notifyAll()
必须在synchronized
代码块或方法中调用,否则会抛出IllegalMonitorStateException
。
- 条件检查:
- 使用
wait()
时,通常需要在while
循环中检查条件,而不是if
,以避免虚假唤醒(spurious wakeup)。
- 使用
- 锁的竞争:
- 被唤醒的线程需要重新竞争锁,因此可能会存在竞争问题。
- 中断处理:
wait()
方法可能会抛出InterruptedException
,需要妥善处理中断。
6. 示例:生产者-消费者模型
class SharedResource {
private int value;
private boolean available = false;
public synchronized void produce(int newValue) {
while (available) {
try {
wait(); // 等待消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
value = newValue;
available = true;
notifyAll(); // 唤醒所有消费者
}
public synchronized int consume() {
while (!available) {
try {
wait(); // 等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll(); // 唤醒所有生产者
return value;
}
}
7. 总结
wait()
、notify()
和notifyAll()
是 Java 中实现线程间通信的核心方法。- 它们必须在同步代码块中使用,且通常与条件检查结合使用。
wait()
用于线程等待,notify()
用于唤醒单个线程,notifyAll()
用于唤醒所有线程。- 适用于生产者-消费者模型、线程池任务调度等场景。
THE END
暂无评论内容