Java多线程wait/notifyAll逻辑触发死锁的缺陷排查
核心缺陷说明
你的代码有3个关键逻辑错误,是导致死锁、逻辑不符合预期的根本原因:
- 第一处致命错误:错误调用
run()而非start()启动线程Thread类的run()是普通方法,直接调用不会创建新的执行线程,只会在当前执行ma()的主线程中同步执行run()内的全部逻辑。代码实际执行流程为:- 主线程调用
thread.run(),直接进入第一个线程的执行逻辑 - 执行完
b.mb1()后,主线程获取object的内置锁,调用object.wait()进入阻塞状态并释放锁 - 此时主线程已经卡在等待状态,后续的
thread1.run()调用、notifyAll()唤醒逻辑永远没有机会执行,程序直接死锁。
- 主线程调用
- 第二处错误:存在竞态条件,唤醒时机无保障
就算把run()改成start()真正启动子线程,现有逻辑也无法保证「两个子线程都进入wait状态后主线程才执行notifyAll()」。子线程启动后的调度时机完全由操作系统决定,完全可能出现主线程先拿到锁执行完notifyAll(),两个子线程之后才进入wait状态的情况,此时子线程会永远等不到唤醒信号,再次死锁。 - 第三处错误:
wait()调用不符合Java规范,未绑定条件判断
Java内置锁的wait()方法存在虚假唤醒问题:线程可能在没有被notify()/notifyAll()唤醒、也没有被中断的情况下从wait状态返回。如果不把wait()放在循环中、每次唤醒后都检查等待条件是否真的满足,会出现线程提前执行后续逻辑的问题。
修正方案
按照你原本的设计逻辑,需要引入共享计数变量标记已经执行完mb1()的线程数,作为wait/notify的判断条件,同时修正线程启动方式。参考实现如下:
public class AImpl{ public static final Object lock = new Object(); // 记录已经执行完mb1的线程数 public static int mb1FinishCount = 0; public static void main(String[] args) throws InterruptedException { BImpl b = new BImpl(); AImpl.ma(b); } public static void ma(B b) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { b.mb1(); synchronized(lock){ mb1FinishCount++; // 只要还有线程没执行完mb1,就等待 while (mb1FinishCount < 2) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); return; } } // 第二个执行完mb1的线程负责唤醒所有等待线程 lock.notifyAll(); } b.mb2(); System.out.println("Thread finished"); } }; Thread thread1 = new Thread() { @Override public void run() { b.mb1(); synchronized(lock){ mb1FinishCount++; while (mb1FinishCount < 2) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); return; } } lock.notifyAll(); } b.mb2(); System.out.println("Thread finished"); } }; // 正确启动两个独立子线程 thread.start(); thread1.start(); // 主线程等待两个线程都执行完mb1再结束 synchronized (lock) { while (mb1FinishCount < 2) { lock.wait(); } } System.out.println("Program finished."); } }
注:如果不需要手动练习wait/notify机制,实际生产代码中直接用
CountDownLatch等JUC工具类实现线程协作会更简洁,也不容易出错。
内容的提问来源于stack exchange,提问作者BlueRyse
相关产品推荐
相关产品推荐

