在Java多线程编程中,线程间的协调与通信是构建稳定并发程序的关键。除了共享内存与锁机制外,Java还提供了一组基于对象监视器(Monitor)的通信原语:wait(),
notify(), 以及 notifyAll()。这三个方法共同构成了线程间“等待-通知”模式的基础。

方法定义与归属
wait():使当前线程暂停执行并进入等待状态,同时释放其持有的对象锁。线程将停留在对象的等待集中,直到被其他线程唤醒。

notify():从在该对象上等待的线程中,任意选择一个并将其移出等待集,使其重新进入锁竞争状态。

notifyAll():唤醒在该对象上等待的所有线程,使它们全部移出等待集并重新竞争锁。

这三个方法并非定义在 Thread 类中,而是位于 Object
类。这是因为锁是关联于对象实例的(每个对象都有一个内置监视器锁),线程通过获取对象的锁来进入同步区域。因此,让线程等待或唤醒的操作自然应该由锁的持有者——对象来提供。

http://qianniu.javastack.cn/18-6-1/82637503.jpg

wait(long timeout) 方法允许设置一个最大等待时长。若超时后仍未被唤醒,线程会自动恢复并尝试重新获取锁。

关键使用原则
必须在同步上下文中调用:调用这些方法的线程必须已经获得了该对象的监视器锁,即只能在 synchronized 方法或同步代码块内部使用。

wait() 会释放锁:调用 wait() 后,线程不仅暂停,还会释放其持有的对象锁,这是实现有效协调的前提。

优先使用 notifyAll():notify() 随机唤醒一个线程,可能导致某些线程“饥饿”或死锁。通常更安全的选择是使用 notifyAll()
,唤醒所有等待线程,让它们公平竞争。

实践示例
java
public class WaitNotifyDemo {
public static void main(String[] args) {
final Object coordinator = new Object();

    Thread waiter = new Thread(() -> {
        synchronized (coordinator) {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Waiter: " + i);
                if (i == 3) {
                    try {
                        System.out.println("Waiter: 即将等待...");
                        coordinator.wait(); // 释放锁并等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("Waiter: 继续完成剩余工作。");
        }
    });

    Thread notifier = new Thread(() -> {
        synchronized (coordinator) {
            try {
                System.out.println("Notifier: 休眠2秒,模拟工作。");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Notifier: 工作完成,唤醒等待者。");
            coordinator.notifyAll(); // 唤醒所有等待线程
        }
    });

    waiter.start();
    notifier.start();
}

}
输出可能类似于:

text
Waiter: 1
Waiter: 2
Waiter: 3
Waiter: 即将等待…
Notifier: 休眠2秒,模拟工作。
Notifier: 工作完成,唤醒等待者。
Waiter: 4
Waiter: 5
Waiter: 继续完成剩余工作。
线程 waiter 在输出到3时进入等待,释放锁。线程 notifier 获取锁后执行任务,最后通过 notifyAll() 唤醒 waiter,使其继续执行。