Java Notifyall方法
Java notifyAll方法
介绍[编辑 | 编辑源代码]
在Java多线程编程中,`notifyAll()`是`Object`类的一个方法,用于唤醒所有正在等待该对象监视器(monitor)的线程。与`notify()`不同,`notify()`仅随机唤醒一个等待线程,而`notifyAll()`会唤醒所有等待线程,让它们竞争获取对象锁。
`notifyAll()`通常与`wait()`方法配合使用,实现线程间的协调。它适用于多线程共享资源时,需要通知多个等待线程的场景,例如生产者-消费者问题或线程池管理。
语法[编辑 | 编辑源代码]
```java public final void notifyAll() ```
工作原理[编辑 | 编辑源代码]
1. 当线程调用`notifyAll()`时,会释放当前持有的对象锁。 2. 所有因调用`wait()`而进入等待状态的线程会被唤醒,并尝试重新获取对象锁。 3. 这些线程会从`wait()`调用处继续执行。
代码示例[编辑 | 编辑源代码]
以下是一个生产者-消费者模型的示例,展示`notifyAll()`的用法:
```java import java.util.LinkedList; import java.util.Queue;
public class ProducerConsumerExample {
private final Queue<Integer> queue = new LinkedList<>(); private final int CAPACITY = 5;
public void produce() throws InterruptedException { int value = 0; while (true) { synchronized (this) { // 如果队列已满,生产者等待 while (queue.size() == CAPACITY) { wait(); } System.out.println("生产者生产: " + value); queue.add(value++); // 通知所有等待的消费者 notifyAll(); Thread.sleep(1000); } } }
public void consume() throws InterruptedException { while (true) { synchronized (this) { // 如果队列为空,消费者等待 while (queue.isEmpty()) { wait(); } int value = queue.poll(); System.out.println("消费者消费: " + value); // 通知所有等待的生产者 notifyAll(); Thread.sleep(1000); } } }
public static void main(String[] args) { ProducerConsumerExample pc = new ProducerConsumerExample(); Thread producerThread = new Thread(() -> { try { pc.produce(); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread consumerThread = new Thread(() -> { try { pc.consume(); } catch (InterruptedException e) { e.printStackTrace(); } }); producerThread.start(); consumerThread.start(); }
} ```
输出示例[编辑 | 编辑源代码]
``` 生产者生产: 0 生产者生产: 1 生产者生产: 2 生产者生产: 3 生产者生产: 4 消费者消费: 0 生产者生产: 5 消费者消费: 1 ... ```
关键点说明[编辑 | 编辑源代码]
1. `wait()`和`notifyAll()`必须在`synchronized`块内调用,否则会抛出`IllegalMonitorStateException`。 2. 使用`while`循环检查条件(如`queue.size() == CAPACITY`),而不是`if`语句,以避免虚假唤醒(spurious wakeup)。 3. `notifyAll()`会唤醒所有等待线程,但只有一个线程能获取锁并执行,其他线程会继续等待。
实际应用场景[编辑 | 编辑源代码]
1. 线程池管理:当任务队列为空时,工作线程会等待。当新任务到达时,主线程调用`notifyAll()`唤醒所有工作线程。 2. 数据库连接池:当所有连接被占用时,请求线程等待。当连接释放时,调用`notifyAll()`唤醒等待线程。 3. 事件驱动系统:多个线程等待某个事件发生,事件触发后调用`notifyAll()`通知所有监听线程。
与notify()的区别[编辑 | 编辑源代码]
| 特性 | notify() | notifyAll() | |--------------------|------------------------------|------------------------------| | 唤醒线程数量 | 1个 | 所有等待线程 | | 适用场景 | 单消费者或明确知道只需唤醒1个 | 多消费者或不确定唤醒数量时 | | 性能影响 | 较低 | 较高(可能引起不必要的竞争) |
数学表示[编辑 | 编辑源代码]
假设有个线程在等待,`notifyAll()`的唤醒效果可以表示为:
而`notify()`的效果是:
注意事项[编辑 | 编辑源代码]
1. 过度使用`notifyAll()`可能导致性能问题,因为所有等待线程会被唤醒并竞争锁。 2. 确保在正确的条件下调用`notifyAll()`,否则可能导致死锁或资源浪费。 3. 在Java 5+中,考虑使用`java.util.concurrent`包中的高级同步工具(如`Condition`),它们提供了更灵活的线程通知机制。
总结[编辑 | 编辑源代码]
`notifyAll()`是Java多线程编程中的重要工具,用于协调多个线程的执行。理解其工作原理和适用场景,可以帮助开发者编写更高效、更安全的并发程序。在实际开发中,应根据具体需求选择`notify()`或`notifyAll()`,并始终结合`wait()`在循环中使用。