使用字符串作为锁时,代码抛出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。
三种场景的差异本质:
- 使用
synchronized(this)时正常:此时线程持有this的监视器锁,调用this.wait()/this.notify()(也就是代码里的wait()/notify())完全符合规则,所以程序能正常运行。 - 使用
new Object()作为lock时正常:大概率是你测试时修改了代码,把wait()/notify()改成了lock.wait()/lock.notify()——此时线程持有lock的锁,调用对应对象的方法合法,所以程序正常。如果代码还是调用this的wait()/notify(),同样会抛出异常。 - 使用字符串
"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
相关产品推荐
相关产品推荐

