跳转到内容

Java Notifyall方法

来自代码酷

Java notifyAll方法

介绍[编辑 | 编辑源代码]

在Java多线程编程中,`notifyAll()`是`Object`类的一个方法,用于唤醒所有正在等待该对象监视器(monitor)的线程。与`notify()`不同,`notify()`仅随机唤醒一个等待线程,而`notifyAll()`会唤醒所有等待线程,让它们竞争获取对象锁。

`notifyAll()`通常与`wait()`方法配合使用,实现线程间的协调。它适用于多线程共享资源时,需要通知多个等待线程的场景,例如生产者-消费者问题或线程池管理。

语法[编辑 | 编辑源代码]

```java public final void notifyAll() ```

工作原理[编辑 | 编辑源代码]

1. 当线程调用`notifyAll()`时,会释放当前持有的对象锁。 2. 所有因调用`wait()`而进入等待状态的线程会被唤醒,并尝试重新获取对象锁。 3. 这些线程会从`wait()`调用处继续执行。

graph TD A[线程调用notifyAll()] --> B[释放对象锁] B --> C[唤醒所有等待线程] C --> D[线程竞争锁] D --> E[一个线程获取锁并继续执行]

代码示例[编辑 | 编辑源代码]

以下是一个生产者-消费者模型的示例,展示`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个 | 多消费者或不确定唤醒数量时 | | 性能影响 | 较低 | 较高(可能引起不必要的竞争) |

数学表示[编辑 | 编辑源代码]

假设有n个线程在等待,`notifyAll()`的唤醒效果可以表示为:

notifyAll(){T1,T2,,Tn} 进入就绪状态

而`notify()`的效果是:

notify(){Tkk[1,n]} 进入就绪状态

注意事项[编辑 | 编辑源代码]

1. 过度使用`notifyAll()`可能导致性能问题,因为所有等待线程会被唤醒并竞争锁。 2. 确保在正确的条件下调用`notifyAll()`,否则可能导致死锁或资源浪费。 3. 在Java 5+中,考虑使用`java.util.concurrent`包中的高级同步工具(如`Condition`),它们提供了更灵活的线程通知机制。

总结[编辑 | 编辑源代码]

`notifyAll()`是Java多线程编程中的重要工具,用于协调多个线程的执行。理解其工作原理和适用场景,可以帮助开发者编写更高效、更安全的并发程序。在实际开发中,应根据具体需求选择`notify()`或`notifyAll()`,并始终结合`wait()`在循环中使用。