notify()
和 notifyAll()
是 Java 中用于线程间通信的方法,这两个方法都用于唤醒正在等待 (wait()
) 的线程。然而,它们在工作方式和应用场景上有一些重要的区别。
notify() vs notifyAll()
-
notify()
:- 唤醒一个正在等待 (
wait()
) 的线程。 - 如果有多个线程在等待,具体唤醒哪一个线程是不确定的。
- 可能导致死锁,如果唤醒的线程不能使等待条件变为真,而其他线程仍然需要等待。
- 唤醒一个正在等待 (
-
notifyAll()
:- 唤醒所有正在等待 (
wait()
) 的线程。 - 确保所有等待的线程都有机会检查等待条件。
- 唤醒所有正在等待 (
示例
假设有一个资源池,其中有一定数量的资源,线程可以从中获取资源。如果资源池为空,线程需要等待直到有资源可用。
java">import java.util.LinkedList;
import java.util.Queue;
public class ResourcePool {
private final Queue<String> pool = new LinkedList<>();
private final int capacity;
public ResourcePool(int capacity) {
this.capacity = capacity;
}
public synchronized void produce(String resource) throws InterruptedException {
while (pool.size() == capacity) {
wait();
}
pool.add(resource);
notifyAll(); // 通知所有等待的线程资源可能可用
}
public synchronized String consume() throws InterruptedException {
while (pool.isEmpty()) {
wait();
}
String resource = pool.poll();
notifyAll(); // 通知所有等待的线程可能有空间可用
return resource;
}
public static void main(String[] args) {
ResourcePool pool = new ResourcePool(2);
// Producer thread
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
pool.produce("Resource " + i);
System.out.println("Produced Resource " + i);
Thread.sleep(500); // 模拟生产时间
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// Consumer thread
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
String resource = pool.consume();
System.out.println("Consumed " + resource);
Thread.sleep(1000); // 模拟消费时间
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
说明
-
while
循环与wait()
:- 在调用
wait()
之前,应该使用while
循环来检查等待条件。这是因为wait()
可能会在条件尚未满足时被中断(例如,由notifyAll()
唤醒,但条件仍未满足)。 - 在
produce()
和consume()
方法中,使用while
循环来确保在调用wait()
前后都检查条件。
- 在调用
-
notify()
可能导致死锁:- 如果我们使用
notify()
而不是notifyAll()
,可能会唤醒一个不能使条件变为真的线程,而其他线程仍然需要等待,导致死锁。 - 在上面的例子中,如果我们使用
notify()
,可能会出现生产者线程唤醒另一个生产者线程(而资源池已满),或者消费者线程唤醒另一个消费者线程(而资源池为空)的情况,导致死锁。
- 如果我们使用
-
notifyAll()
确保所有线程都有机会检查条件:- 使用
notifyAll()
可以确保所有等待的线程都被唤醒,并且都有机会检查等待条件是否满足。 - 在上面的代码中,
notifyAll()
确保无论是生产者还是消费者线程,都能在资源池状态改变时被唤醒,并检查条件是否满足。
- 使用
结语
- 使用
notify()
需要非常小心,确保唤醒的线程能够正确处理接下来的事项,否则可能导致死锁。 notifyAll()
更加安全和保守,适用于大多数场景,确保所有等待的线程都有机会检查条件。- 在使用
wait()
时,应配合while
循环使用,以确保在条件不满足时能够重新进入等待状态。