Java多线程问题:notifyAll()无法在共享锁上唤醒线程的原因排查
问题根源:误解了
wait()/notify()和InterruptedException的关系 兄弟,你这里的核心问题完全是对Java线程协作机制的误解——notifyAll()根本不会触发InterruptedException!这个异常只有当线程被主动调用interrupt()方法时才会抛出,和唤醒等待线程的notify系列方法半毛钱关系都没有。你的代码逻辑错把唤醒当成了中断信号,自然会导致两个线程都陷入等待,程序阻塞。
先给你拆解下现有代码的死循环过程:
- 前端线程先进入同步块,调用
notifyAll()(这时候后端线程还没进入同步块,所以没效果),然后调用wait()进入等待。 - 后端线程接着进入同步块,调用
notifyAll()(这时候前端线程在等待,会被唤醒,但前端线程醒过来后会检查allCaughtUp还是false,于是又走一遍循环:先notifyAll(),再wait())。 - 现在两个线程都卡在
wait()上,没人再调用notifyAll()来唤醒它们,程序直接阻塞。
正确实现:用状态变量配合wait()/notify()做线程协作
你的业务场景是典型的前后端线程交替协作:前端等后端准备数据,后端等前端完成输入再准备下一批。我们应该用明确的状态变量来标记阶段,配合wait()/notify()来实现,而不是错误地依赖异常。
这里给你写个修正后的示例代码,完全贴合你的业务场景:
public class FrontBackCooperation { // 共享锁对象 static final Object lock = new Object(); // 标记后端是否准备好数据 static boolean dataReady = false; // 标记前端是否完成输入 static boolean inputDone = false; // 模拟多轮数据交互,你可以根据需求调整 static final int ROUND = 3; public static void main(String[] args) { // 启动后端线程 new Thread(() -> backendTask()).start(); // 前端主线程执行任务 frontendTask(); } /** * 前端线程逻辑:等待数据 -> 处理展示/用户输入 -> 通知后端 */ private static void frontendTask() { for (int i = 1; i <= ROUND; i++) { synchronized (lock) { // 等待后端准备好数据,用while防止虚假唤醒 while (!dataReady) { try { lock.wait(); } catch (InterruptedException e) { // 这里的异常是处理线程被中断的情况,不是协作信号 Thread.currentThread().interrupt(); return; } } // 模拟前端处理数据/等待用户输入 System.out.printf("第%d轮:前端拿到数据,展示并等待用户输入...%n", i); try { Thread.sleep(1000); // 模拟用户输入耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } // 标记输入完成,唤醒后端线程 inputDone = true; dataReady = false; lock.notifyAll(); } } System.out.println("前端任务全部完成"); } /** * 后端线程逻辑:准备数据 -> 通知前端 -> 等待输入完成 -> 准备下一批 */ private static void backendTask() { for (int i = 1; i <= ROUND; i++) { synchronized (lock) { // 模拟后端准备数据 System.out.printf("第%d轮:后端正在准备数据...%n", i); try { Thread.sleep(1500); // 模拟数据准备耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } // 标记数据准备好,唤醒前端线程 dataReady = true; inputDone = false; lock.notifyAll(); // 等待前端完成输入 while (!inputDone) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } System.out.printf("第%d轮:后端收到输入完成信号,准备下一批数据...%n", i); } } System.out.println("后端任务全部完成"); } }
关键要点总结
- 永远在
while循环中检查状态:这是为了处理「虚假唤醒」——线程可能会在没有被notify的情况下醒来,必须重新检查状态确认是否可以继续执行。 notify/notifyAll的作用是唤醒等待线程:被唤醒的线程会从wait()方法处继续执行,然后重新检查状态,而不是抛出异常。InterruptedException是用来处理线程中断的:比如外部需要终止线程时调用interrupt(),这时候才会触发这个异常,不要用它来做线程协作的信号。
内容的提问来源于stack exchange,提问作者Thorbear
相关产品推荐
相关产品推荐

