You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线程执行出现IllegalStateMonitorException的锁机制疑问

问题分析与解答

核心误区:synchronized锁的是对象,不是变量本身

你代码里的flag是引用类型变量,synchronized (flag)实际锁住的是flag初始指向的那个Boolean(true)对象,而非flag这个变量本身。

线程2执行flag = false时,只是把flag的引用从原来的Boolean(true)对象,切换到了常量池里的Boolean(false)对象——这个操作完全不需要获取原来那个Boolean(true)对象的锁,所以线程2根本不会被线程1阻塞,直接就能完成赋值。

为什么抛出IllegalStateMonitorException?

线程1的执行流程会触发异常:

  1. 进入synchronized (flag)块,此时持有初始Boolean(true)对象的锁
  2. 执行Thread.sleep(5000),sleep不会释放锁,这段时间线程1一直持有原对象的锁
  3. 这期间线程2已经把flag的引用改成了Boolean(false)对象
  4. 线程1醒来后调用flag.wait(),此时flag指向的是新的Boolean(false)对象,但线程1并没有持有这个新对象的锁
  5. 根据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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 21:35:16