Java中的wait()、notify()和notifyAll()方法详解
字数 1294 2025-11-06 12:41:12

Java中的wait()、notify()和notifyAll()方法详解

描述
wait()notify()notifyAll()是Java中用于线程间协作的底层机制,它们定义在Object类中,必须结合synchronized关键字使用。这些方法通过对象的监视器锁(Monitor)实现线程的等待与唤醒,常用于生产者-消费者模型或条件判断的场景。


1. 方法的基本作用

  • wait():当前线程释放锁并进入等待状态,直到其他线程调用同一对象的notify()notifyAll()方法,或被中断。
  • notify():随机唤醒一个正在等待该对象锁的线程(多个等待线程时唤醒其中一个)。
  • notifyAll():唤醒所有正在等待该对象锁的线程,这些线程将竞争锁。

关键点

  • 调用这些方法前,线程必须持有该对象的监视器锁(即必须在synchronized代码块或方法内)。
  • 调用wait()后,线程会释放锁,允许其他线程获取锁并执行。

2. 底层原理:监视器模型
每个Java对象都有一个关联的监视器锁(Monitor)和一个等待队列(Wait Set)。

  • 当线程调用wait()时,会释放锁并进入等待队列。
  • 当其他线程调用notify()时,JVM从等待队列中移出一个线程到入口队列(Entry Set),使其有机会重新竞争锁。
  • 被唤醒的线程需要重新获取锁后才能继续执行。

3. 使用步骤示例(生产者-消费者模型)
以下代码展示一个简单的队列,当队列为空时消费者等待,队列满时生产者等待:

public class WaitNotifyExample {
    private final Queue<String> queue = new LinkedList<>();
    private final int MAX_SIZE = 5;

    public synchronized void produce(String item) throws InterruptedException {
        while (queue.size() == MAX_SIZE) {
            wait(); // 队列已满,生产者等待
        }
        queue.add(item);
        System.out.println("Produced: " + item);
        notifyAll(); // 唤醒所有等待线程(消费者或生产者)
    }

    public synchronized String consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 队列为空,消费者等待
        }
        String item = queue.poll();
        System.out.println("Consumed: " + item);
        notifyAll(); // 唤醒其他线程
        return item;
    }
}

步骤解析

  1. 生产者线程
    • 获取锁后检查队列是否已满,若满则调用wait()释放锁并等待。
    • 生产数据后调用notifyAll()唤醒可能等待的消费者。
  2. 消费者线程
    • 获取锁后检查队列是否为空,若空则调用wait()释放锁并等待。
    • 消费数据后调用notifyAll()唤醒可能等待的生产者。

注意

  • 必须用while而非if检查条件,防止虚假唤醒(Spurious Wakeup)导致状态错误。
  • 调用notifyAll()而非notify()可避免线程饥饿(如只唤醒同类线程)。

4. 常见问题与注意事项

  • IllegalMonitorStateException异常:未在同步代码块中调用这些方法时抛出。
  • 中断处理wait()方法可能被interrupt()中断,需捕获InterruptedException
  • 超时等待:使用wait(long timeout)避免永久等待。
  • LockCondition的对比:JUC中的Condition提供了更灵活的等待/唤醒机制(如多条件队列)。

5. 总结
wait()/notify()是Java多线程协作的基石,核心在于:

  • 通过对象监视器锁实现线程同步;
  • while循环避免虚假唤醒;
  • 协调线程间状态依赖关系。
    掌握这些方法有助于理解更高级的并发工具(如BlockingQueue)的底层实现。
Java中的wait()、notify()和notifyAll()方法详解 描述 wait() 、 notify() 和 notifyAll() 是Java中用于线程间协作的底层机制,它们定义在 Object 类中,必须结合 synchronized 关键字使用。这些方法通过对象的监视器锁(Monitor)实现线程的等待与唤醒,常用于生产者-消费者模型或条件判断的场景。 1. 方法的基本作用 wait() :当前线程释放锁并进入等待状态,直到其他线程调用同一对象的 notify() 或 notifyAll() 方法,或被中断。 notify() :随机唤醒一个正在等待该对象锁的线程(多个等待线程时唤醒其中一个)。 notifyAll() :唤醒所有正在等待该对象锁的线程,这些线程将竞争锁。 关键点 : 调用这些方法前,线程必须持有该对象的监视器锁(即必须在 synchronized 代码块或方法内)。 调用 wait() 后,线程会释放锁,允许其他线程获取锁并执行。 2. 底层原理:监视器模型 每个Java对象都有一个关联的监视器锁(Monitor)和一个等待队列(Wait Set)。 当线程调用 wait() 时,会释放锁并进入等待队列。 当其他线程调用 notify() 时,JVM从等待队列中移出一个线程到入口队列(Entry Set),使其有机会重新竞争锁。 被唤醒的线程需要重新获取锁后才能继续执行。 3. 使用步骤示例(生产者-消费者模型) 以下代码展示一个简单的队列,当队列为空时消费者等待,队列满时生产者等待: 步骤解析 : 生产者线程 : 获取锁后检查队列是否已满,若满则调用 wait() 释放锁并等待。 生产数据后调用 notifyAll() 唤醒可能等待的消费者。 消费者线程 : 获取锁后检查队列是否为空,若空则调用 wait() 释放锁并等待。 消费数据后调用 notifyAll() 唤醒可能等待的生产者。 注意 : 必须用 while 而非 if 检查条件,防止 虚假唤醒 (Spurious Wakeup)导致状态错误。 调用 notifyAll() 而非 notify() 可避免线程饥饿(如只唤醒同类线程)。 4. 常见问题与注意事项 IllegalMonitorStateException异常 :未在同步代码块中调用这些方法时抛出。 中断处理 : wait() 方法可能被 interrupt() 中断,需捕获 InterruptedException 。 超时等待 :使用 wait(long timeout) 避免永久等待。 与 Lock 和 Condition 的对比:JUC中的 Condition 提供了更灵活的等待/唤醒机制(如多条件队列)。 5. 总结 wait()/notify() 是Java多线程协作的基石,核心在于: 通过对象监视器锁实现线程同步; 用 while 循环避免虚假唤醒; 协调线程间状态依赖关系。 掌握这些方法有助于理解更高级的并发工具(如 BlockingQueue )的底层实现。