You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么Java中notify()仅唤醒最后调用wait()的线程而非最先调用的?

问题根因

Java语言规范从未要求notify()方法按照线程调用wait()的先后顺序唤醒等待线程,具体唤醒策略完全由JVM实现决定。你使用的OpenJDK/OracleJDK默认的HotSpot虚拟机,notify()默认采用非公平唤醒策略,会优先唤醒距离当前时间最近、刚进入等待队列的线程,这就是你观测到先调用wait()的线程拿不到唤醒机会、出现饥饿的核心原因。

你代码中两个线程互相唤醒的现象是该策略的典型表现:持有锁的线程调用notify()之后会立即调用wait()释放锁,此时刚进入等待队列的线程会被优先唤醒,几次循环后就会形成两个线程固定互唤醒的闭环,剩下的线程始终无法被选中。

你遇到的现象和Windows 10操作系统、JDK 15版本没有直接关联,切换其他版本的HotSpot JDK大概率也会复现同样的问题。

解决方案

如果需要实现严格按等待顺序唤醒的公平策略,可以选择以下两种修改方案:

  • 把notify()替换为notifyAll():每次唤醒所有等待在该锁对象上的线程,所有线程共同抢占锁,可解决饥饿问题,但会产生大量无效唤醒的性能开销,仅适合小并发量的场景。
  • 改用公平模式的ReentrantLock配合Condition实现等待通知:初始化ReentrantLock时设置fair = true开启公平模式,配套的Condition对象的signal()方法会严格按照线程进入等待队列的顺序唤醒,不会出现饥饿问题,性能也更稳定。
    公平实现的核心示例代码如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static final int t_count = 4;
    // 替换原内置锁,开启公平模式
    public static final ReentrantLock lock = new ReentrantLock(true);
    public static final Condition condition = lock.newCondition();
    public static volatile int id = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread t[] = new Thread[t_count];
        for (int i = 0; i < t_count; i++) {
            t[i] = new MyThread();
            t[i].setUncaughtExceptionHandler((t1, e) -> e.printStackTrace());
            t[i].start();
        }
        t[t_count - 1].join();
    }

    public static class MyThread extends Thread {
        static int integer = 0;
        static int count = 0;
        int i;

        public MyThread() {
            i = integer++;
            System.out.println(i + " initial" + " " + this);
        }

        @Override
        public void run() {
            try {
                lock.lock();
                while (true) {
                    System.out.println(i + " is here");
                    count += 1;
                    if (count >= 40) {
                        condition.signal();
                        System.out.println(i + " return");
                        return;
                    }
                    if (id == i) {
                        System.out.println(i + " do work");
                        System.out.flush();
                        id = (id + 1) % t_count;
                    }
                    System.out.println(i + " notify");
                    condition.signal();
                    System.out.println(i + " wait");
                    condition.await();
                    System.out.println(i + " notified");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者desperateLord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 22:54:08