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

Java多线程中wait()与notify()搭配使用执行逻辑疑问求解

问题核心原因分析

1. wait/notify顺序差异的本质

你要实现的是字母、数字交替打印的效果,两个线程共用同一个Jobs实例作为锁,核心逻辑是每次执行完一次打印,必须把执行权交给对方,自己进入等待状态。
当前可正常运行的版本执行流程如下:

  • 默认情况下t1(打印字母线程)先抢到锁,打印字符a,调用wait()释放锁,自身进入等待队列
  • t2(打印数字线程)拿到锁,打印数字1,调用notify()唤醒等待的t1,随后调用wait()释放锁,自身进入等待
  • t1被唤醒后重新拿到锁,执行sleep(1000)(sleep不会释放锁),调用notify()唤醒等待的t2,进入下一轮循环打印字符b,再调用wait()释放锁
  • 以此类推,两个线程交替执行,刚好跑完26轮循环
    printAlphabet先wait后notify、printDigits先notify后wait的顺序,是适配t1优先抢到锁的场景:第一个完成打印的线程需要先释放锁给对方,对方拿到锁完成打印后,要先唤醒等待的第一个线程再自己挂起,避免第一个线程永久等待。

2. 单方向调用wait/notify卡住的原因

仅在printAlphabet调用wait()、printDigits调用notify()的场景,执行流程如下:

  • t1先抢到锁打印a,调用wait()释放锁进入等待
  • t2拿到锁打印1,调用notify()唤醒t1,但t2自身没有调用wait(),会持续持有锁跑完26次循环,把1到26全部打印完成后,才会退出同步块释放锁
  • 此时t1才会被调度拿到锁,打印b,随后调用wait()进入等待,但t2已经执行完整个方法退出,没有其他线程会调用notify()唤醒t1,程序就会永久卡住。

补充优化建议

你当前的代码存在小隐患:如果t2先抢到锁,会先打印1,调用notify后自身wait,t1拿到锁打印a后调用wait,两个线程会同时进入等待状态直接死锁。建议新增状态标记变量,搭配while循环判断执行条件(避免虚假唤醒),优化后代码如下:

class Jobs {
    // true=当前该打印字母,false=当前该打印数字
    private boolean printAlphabetFlag = true;
    public void printAlphabet() throws Exception {
        char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
        synchronized (this) {
            for (int i = 0; i < alphabet.length; i++) {
                // 不该自己执行就持续等待
                while(!printAlphabetFlag) {
                    wait();
                }
                System.out.print(alphabet[i] + " ");
                // 修改执行标记,通知对方执行
                printAlphabetFlag = false;
                notify();
                Thread.sleep(1000);
            }
        }
    }

    public void printDigits() throws Exception {
        String[] digits = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26".split(" ");
        synchronized (this) {
            for (int i = 0; i < digits.length; i++) {
                while(printAlphabetFlag) {
                    wait();
                }
                System.out.print(digits[i] + " ");
                printAlphabetFlag = true;
                notify();
                Thread.sleep(1000);
            }
        }
    }
}

优化后的版本不依赖线程启动顺序,无论哪个线程先抢到锁,都能按a 1 b 2 ...的预期顺序执行,不会出现死锁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:45:04