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
相关产品推荐
相关产品推荐

