线程执行出现IllegalStateMonitorException的锁机制疑问
问题分析与解答
核心误区:synchronized锁的是对象,不是变量本身
你代码里的flag是引用类型变量,synchronized (flag)实际锁住的是flag初始指向的那个Boolean(true)对象,而非flag这个变量本身。
线程2执行flag = false时,只是把flag的引用从原来的Boolean(true)对象,切换到了常量池里的Boolean(false)对象——这个操作完全不需要获取原来那个Boolean(true)对象的锁,所以线程2根本不会被线程1阻塞,直接就能完成赋值。
为什么抛出IllegalStateMonitorException?
线程1的执行流程会触发异常:
- 进入
synchronized (flag)块,此时持有初始Boolean(true)对象的锁 - 执行
Thread.sleep(5000),sleep不会释放锁,这段时间线程1一直持有原对象的锁 - 这期间线程2已经把
flag的引用改成了Boolean(false)对象 - 线程1醒来后调用
flag.wait(),此时flag指向的是新的Boolean(false)对象,但线程1并没有持有这个新对象的锁 - 根据Java规则,调用
Object.wait()必须先持有该对象的锁,否则直接抛出IllegalStateMonitorException
线程1什么时候会释放锁?
正常情况下,线程1只有两种场景会释放锁:
- 执行完
synchronized块的全部代码 - 调用当前持有的锁对象的
wait()方法(此时会释放锁,进入等待状态)
但你的代码里,线程1还没来得及调用原锁对象的wait(),flag的引用就已经被线程2替换了,后续调用的是新对象的wait(),完全不满足锁持有条件,直接抛出异常。
修复方案
要避免这类问题,核心是让锁对象的引用不会被修改,同时确保wait/notify操作的是同一个锁对象。这里提供两种常见修复思路:
方案1:使用独立的不可变锁对象
单独声明一个不会被替换的锁对象,所有同步操作都基于这个对象:
class CorrectSynchronization { private boolean flag = true; private final Object lock = new Object(); // 固定锁对象,不会被替换 public void example() throws InterruptedException { Thread t1 = new Thread(new Runnable() { public void run() { synchronized (lock) { try { while (flag) { System.out.println("First thread about to sleep"); Thread.sleep(5000); System.out.println("Woke up and about to invoke wait()"); lock.wait(); // 对固定锁对象调用wait } } catch (InterruptedException ie) { ie.printStackTrace(); } } } }); Thread t2 = new Thread(new Runnable() { public void run() { synchronized (lock) { flag = false; lock.notify(); // 唤醒等待固定锁对象的线程 System.out.println("Boolean assignment done."); } } }); t1.start(); Thread.sleep(1000); t2.start(); t1.join(); t2.join(); } }
方案2:使用基本类型+volatile
把flag改成基本类型boolean(避免引用替换),同时配合独立锁对象控制同步逻辑,用volatile保证多线程间的可见性。
内容的提问来源于stack exchange,提问作者arp5
相关产品推荐
相关产品推荐

