使用两个线程打印奇偶数字时抛出IllegalMonitorStateException问题排查
问题分析与解决方案
兄弟,你遇到的IllegalMonitorStateException是线程同步里的典型坑,我来给你拆解问题出在哪,再给你修正后的代码:
为什么会抛出这个异常?
这个异常的核心原因很明确:当你调用wait()、notify()或notifyAll()方法时,当前线程并没有持有该对象的监视器锁——简单说就是你没在synchronized块/方法里操作这个对象,就敢调用这些需要锁权限的方法。
结合你给出的代码片段,大概率踩了这两个坑:
- 用了
Boolean evenFlag或者Integer sharedCount作为锁对象,但这俩都是不可变类型。比如你执行evenFlag = !evenFlag时,会生成一个新的Boolean对象,后续的synchronized块就会锁定不同的对象,导致wait/notify时的锁完全不匹配。 - 可能你的
synchronized块锁定的对象,和调用wait/notify的对象不是同一个;甚至直接在无锁保护的地方调用了wait/notify。
修正后的代码示例
我给你调整了代码,用专门的共享锁对象来做同步,同时修正了线程间的协作逻辑:
package com.rishi.threading; public class OddEvenPrinter { // 用一个专门的、可变的锁对象,彻底避免不可变类型的坑 private final Object lock = new Object(); private int sharedCount = 1; private static final int MAX_NUM = 20; // 打印到20为止 // 打印奇数的线程 class OddThread extends Thread { @Override public void run() { while (sharedCount <= MAX_NUM) { synchronized (lock) { // 当当前数是偶数时,等待偶数线程处理完 while (sharedCount % 2 == 0) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } // 打印奇数并自增 System.out.println("奇数线程输出: " + sharedCount); sharedCount++; // 通知偶数线程可以工作了 lock.notify(); } } } } // 打印偶数的线程 class EvenThread extends Thread { @Override public void run() { while (sharedCount <= MAX_NUM) { synchronized (lock) { // 当当前数是奇数时,等待奇数线程处理完 while (sharedCount % 2 != 0) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } // 打印偶数并自增 System.out.println("偶数线程输出: " + sharedCount); sharedCount++; // 通知奇数线程可以工作了 lock.notify(); } } } } public static void main(String[] args) { OddEvenPrinter printer = new OddEvenPrinter(); printer.new OddThread().start(); printer.new EvenThread().start(); } }
关键修正点说明
- 用专门的锁对象:创建了一个
Object lock作为共享锁,它是可变的,不会因为值的变化变成新对象,确保所有线程都锁定同一个对象。 - 锁内调用wait/notify:所有的
wait()和notify()都在synchronized(lock)的保护下执行,保证当前线程持有锁权限。 - 用while循环判断条件:避免线程被虚假唤醒(线程被唤醒后,条件可能已经变化,必须重新检查)。
- 抛弃不可变锁对象:Integer、Boolean这类不可变类型,每次赋值都会生成新对象,绝对不能用来做同步锁。
内容的提问来源于stack exchange,提问作者Rishi
相关产品推荐
相关产品推荐

