为何锁定myInt时代码抛出java.lang.IllegalMonitorStateException?
为什么锁定Integer类型的myInt会抛出IllegalMonitorStateException?
问题重现
以下代码尝试通过锁定myInt实现线程交替打印,但运行时抛出java.lang.IllegalMonitorStateException:
package Thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultiThreadingTest1 { public Integer myInt = 0; public Object lock = new Object(); public void alternativePrinting() throws InterruptedException { Thread thread1 = new Thread(() -> { while(myInt <= 100) synchronized (myInt){ if(myInt % 2 == 0) { System.out.println(Thread.currentThread().getName() + " prints " + myInt); myInt++; myInt.notify(); } else try { myInt.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(() -> { while(myInt <= 100) synchronized (myInt){ if(myInt % 2 == 1) { System.out.println(Thread.currentThread().getName() + " prints " + myInt); myInt++; myInt.notify(); } else try { myInt.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread1.start(); thread2.start(); } public static void main(String[] args) throws InterruptedException { MultiThreadingTest1 multiThreadingTest1 = new MultiThreadingTest1(); multiThreadingTest1.alternativePrinting(); } }
异常信息
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.base/java.lang.Object.notify(Native Method) at Thread.MultiThreadingTest1.lambda$alternativePrinting$0(MultiThreadingTest1.java:18) at java.base/java.lang.Thread.run(Thread.java:835) Exception in thread "Thread-1" java.lang.IllegalMonitorStateException at java.base/java.lang.Object.notify(Native Method) at Thread.MultiThreadingTest1.lambda$alternativePrinting$1(MultiThreadingTest1.java:34) at java.base/java.lang.Thread.run(Thread.java:835)
核心原因分析
Integer是不可变类,
myInt++会替换对象引用
Java中的Integer是不可变对象,myInt++的本质是执行myInt = Integer.valueOf(myInt.intValue() + 1),这会创建一个全新的Integer对象,并让myInt变量指向这个新对象。当你进入synchronized (myInt)块时,锁定的是当时myInt指向的旧Integer实例,但执行myInt++后,后续调用myInt.notify()或myInt.wait()时,操作的是新的Integer对象,而当前线程并没有持有这个新对象的锁,直接违反了wait/notify的调用规则,因此抛出异常。synchronized锁的是对象实例,不是变量
synchronized块锁定的是括号内对象引用当前指向的实例,而不是变量本身。当myInt的引用被替换后,线程持有的锁是旧对象的,但后续操作的是新对象,自然不满足“调用wait/notify必须持有该对象锁”的要求。对比使用lock对象的情况
当使用Object lock = new Object()作为锁时,lock变量的引用从未改变,所有synchronized块都锁定同一个对象实例,wait/notify也都是在持有该对象锁的前提下调用,完全符合规则,因此不会抛出异常。
内容的提问来源于stack exchange,提问作者ywy144
相关产品推荐
相关产品推荐

