notify() 和 notifyAll() 的核心区别是什么
notify() 随机唤醒一个等待线程,而 notifyall() 唤醒所有等待线程。调用 notify() 时若选中的线程无法继续执行(如条件不满足),会导致死锁或资源闲置;使用 notifyall() 则确保所有相关线程都能重新检查条件并竞争锁,从而避免遗漏。在多线程协作中,除非能绝对保证任意一个被唤醒的线程都能处理当前任务,否则应优先使用 notifyall()。

public class NotificationDemo {
private static final Object lock = new Object();
private static boolean conditionMet = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (lock) {
while (!conditionMet) {
try { lock.wait(); } catch (InterruptedException e) {}
}
System.out.println("Thread-0: Condition met, processing.");
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
while (!conditionMet) {
try { lock.wait(); } catch (InterruptedException e) {}
}
System.out.println("Thread-1: Condition met, processing.");
}
});
t1.start();
t2.start();
Thread.sleep(100);
synchronized (lock) {
conditionMet = true;
lock.notify(); // 仅唤醒一个线程
}
t1.join();
t2.join();
}
}
运行上述代码,控制台通常只会打印其中一条 "Condition met" 信息,另一条线程将永久阻塞在 wait() 处,因为被唤醒的线程可能不是真正需要处理该条件的线程,或者即使它处理了,另一个线程也未被通知。
什么情况下必须使用 notifyAll()
当存在多个等待线程且它们对同一条件的响应逻辑不同,或者唤醒一个线程不足以推进系统状态时,必须使用 notifyAll()。这常见于生产者-消费者模型中既有生产者又有消费者,或者多个线程等待同一个资源池但处理能力不同的场景。使用 notifyAll() 后,所有线程都会从 wait() 返回并重新获取锁,此时必须通过 while 循环再次验证条件,只有条件满足的线程才会继续执行,其余线程会再次进入等待。

public class NotifyAllDemo {
private static final Object lock = new Object();
private static int resourceCount = 0;
private static final int MAX = 5;
public static void main(String[] args) {
Thread producer = new Thread(() -> {
synchronized (lock) {
while (resourceCount {
synchronized (lock) {
while (resourceCount > 0) {
if (resourceCount > 0) {
resourceCount--;
System.out.println("Consumed: " + resourceCount);
lock.notifyAll();
} else {
try { lock.wait(); } catch (InterruptedException e) {}
}
}
}
});
producer.start();
consumer.start();
}
}
在此示例中,生产者和消费者共享 resourceCount。如果生产者使用 notify(),可能会意外唤醒另一个生产者导致其再次等待,或者唤醒消费者但消费者发现无资源可消费而再次等待,造成效率低下或死锁。notifyAll() 确保双方都能感知状态变化并做出正确反应。
基于程序中 wait() 后的条件检查具体行为
在调用 notify() 或 notifyAll() 后,被唤醒的线程并不会立即继续执行后续代码,而是必须先重新获取监视器锁。一旦获取锁,线程会从 wait() 方法返回,但此时它必须重新评估等待条件。这是因为在唤醒到重新获取锁的这段时间内,其他线程可能已经修改了条件状态。因此,Java 最佳实践要求始终在 while 循环中调用 wait(),而不是 if 语句,以确保在条件未真正满足时不会错误地退出等待状态。
,
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











