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

使用字符串作为锁时,代码抛出IllegalMonitorException的原因?

问题

我尝试实现两个线程交替打印0到20的偶数与奇数,分别由偶数线程和奇数线程处理,实现代码如下:

class EvenOddPrint {
  public Object lock = "lock";
  public boolean isEven = true;
  public void evenNumber() 
  {
   for(int i = 0; i <= 20; i++) 
     {
      synchronized (lock)
      {
        while (!isEven) {
          try {
            wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
        if (i % 2 == 0) {
          System.out.println("Even Thread and my even number is  " + i);
          isEven = false;
          notify();
        }
      }
    }
}

  public void oddNumber() {
    boolean str = false;
    for (int i = 0; i <= 20; i++) {
      synchronized (lock) {
        while (isEven) {
          try {
            wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        if (i % 2 != 0) {
          System.out.println("Odd thread and my Odd number is " + i);
          isEven = true;
          notify();
        }
      }
    }
  }
}

public class ThreadExercisesClass {
  public static void main(String args[]) throws InterruptedException {
    EvenOddPrint e = new EvenOddPrint();
    Thread t1 = new Thread(() -> e.evenNumber());
    Thread t2 = new Thread(() -> e.oddNumber());
    t1.start();
    t2.start();
  }
}

当使用synchronized(this)或将lock设为new Object()时,代码可正常运行;但使用字符串"lock"作为锁对象时,程序抛出如下异常:

Even Thread and my even number is 0
Odd thread and my Odd number is 1
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread is not owner
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException: current thread is not owner

请求解释为何会出现该异常?

原因分析

问题的核心是你违反了Java中wait()/notify()方法的调用规则:调用某个对象的wait()或notify()时,当前线程必须持有该对象的监视器锁。

你的代码在synchronized(lock)块里调用的wait()和notify(),默认是针对当前EvenOddPrint实例(也就是this)的方法,但此时线程持有的是lock对象的监视器锁,而非this的——这就导致线程没有权限调用this的等待/通知方法,触发IllegalMonitorStateException。

三种场景的差异本质:

  1. 使用synchronized(this)时正常:此时线程持有this的监视器锁,调用this.wait()/this.notify()(也就是代码里的wait()/notify())完全符合规则,所以程序能正常运行。
  2. 使用new Object()作为lock时正常:大概率是你测试时修改了代码,把wait()/notify()改成了lock.wait()/lock.notify()——此时线程持有lock的锁,调用对应对象的方法合法,所以程序正常。如果代码还是调用this的wait()/notify(),同样会抛出异常。
  3. 使用字符串"lock"作为lock时抛出异常:字符串常量池会让"lock"成为全局唯一的实例,但本质问题还是你调用了this的wait()/notify()而非lock的方法。这里异常更快暴露,可能是因为字符串常量的全局唯一性,JVM对其监视器的管理更严格,或者存在其他未知代码也持有该字符串的锁,导致监视器持有关系被打破。

修复方案

只需要在synchronized(lock)块内,明确调用lock对象的wait()和notify()方法,替换默认的this方法:

// 偶数线程中的修改
synchronized (lock) {
  while (!isEven) {
    try {
      lock.wait(); // 替换为lock.wait()
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  if (i % 2 == 0) {
    System.out.println("Even Thread and my even number is  " + i);
    isEven = false;
    lock.notify(); // 替换为lock.notify()
  }
}

// 奇数线程同理,把wait()/notify()改为lock.wait()/lock.notify()

另外不推荐用字符串常量作为锁对象,因为它是全局唯一的,可能会被其他无关代码意外复用,引发线程安全问题。

内容的提问来源于stack exchange,提问作者sumanth paramkusam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:36