zoukankan      html  css  js  c++  java
  • Java问题记录——IllegalMonitorStateException

    Java问题记录——IllegalMonitorStateException

    摘要:本文主要分析了IllegalMonitorStateException的产生原因。

    部分内容来自以下博客:

    https://blog.csdn.net/historyasamirror/article/details/6709693

    锁对象发生了改变

    在测试多线程通信的代码时,出现了这个异常。

    代码分析

    代码如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         DemoThread demoThread = new DemoThread();
     4         Thread thread1 = new Thread(demoThread);
     5         Thread thread2 = new Thread(demoThread);
     6         thread1.start();
     7         thread2.start();
     8     }
     9 }
    10 
    11 class DemoThread implements Runnable {
    12     private Integer num = 1;
    13 
    14     @Override
    15     public void run() {
    16         while (true) {
    17             synchronized (num) {
    18                 num.notify();
    19                 if (num <= 10) {
    20                     System.out.println(Thread.currentThread().getName() + " >>> " + num++);
    21                     try {
    22                         num.wait();
    23                     } catch (InterruptedException e) {
    24                         e.printStackTrace();
    25                     }
    26                 }
    27             }
    28         }
    29     }
    30 }

    运行结果如下:

     1 Thread-0 >>> 1
     2 Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
     3     at java.lang.Object.notify(Native Method)
     4     at com.iyao.ide.engine.task.DemoThread.run(Demo.java:22)
     5     at java.lang.Thread.run(Thread.java:745)
     6 Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
     7     at java.lang.Object.wait(Native Method)
     8     at java.lang.Object.wait(Object.java:502)
     9     at com.iyao.ide.engine.task.DemoThread.run(Demo.java:26)
    10     at java.lang.Thread.run(Thread.java:745)

    说明

    在网上查找资料,发现需要在调用wait()或者notify()之前,必须使用synchronized语义绑定住被wait/notify的对象。

    可问题是,在上面的代码中,已经对num这个变量使用了synchronzied,然后才调用的num.wait()。按理不应该抛出这个异常。

    真正的问题在于num这个变量是一个Integer,并且,在调用num.wait()之前,num执行了一次自增操作。

    Integer型变量在执行自增的时候,其实是创建了一个新的对象。简单的说,在自增的之前和之后,num并不是同一个对象。

    synchronzied(num)绑定的是旧的Integer对象,而num.wait()使用的是新的Integer对象。由于新的Integer对象并没有使用synchronzied进行同步,所以系统抛出了IllegalMonitorStateException异常。

    相同的悲剧还有可能出现在num是Boolean或者String类型的时候。

    一个解决方案是采用java.util.concurrent.atomic中对应的类型,比如这里就应该是AtomicInteger。采用AtomicInteger类型,可以保证对它的修改不会产生新的对象。

    解决方案

    代码修改后如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         DemoThread demoThread = new DemoThread();
     4         Thread thread1 = new Thread(demoThread);
     5         Thread thread2 = new Thread(demoThread);
     6         thread1.start();
     7         thread2.start();
     8     }
     9 }
    10 
    11 class DemoThread implements Runnable {
    12     private AtomicInteger num = new AtomicInteger(1);
    13 
    14     @Override
    15     public void run() {
    16         while (true) {
    17             synchronized (num) {
    18                 num.notify();
    19                 if (num.intValue() <= 10) {
    20                     System.out.println(Thread.currentThread().getName() + " >>> " + num.getAndAdd(1));
    21                     try {
    22                         num.wait();
    23                     } catch (InterruptedException e) {
    24                         e.printStackTrace();
    25                     }
    26                 }
    27             }
    28         }
    29     }
    30 }

    运行结果如下:

     1 Thread-0 >>> 1
     2 Thread-1 >>> 2
     3 Thread-0 >>> 3
     4 Thread-1 >>> 4
     5 Thread-0 >>> 5
     6 Thread-1 >>> 6
     7 Thread-0 >>> 7
     8 Thread-1 >>> 8
     9 Thread-0 >>> 9
    10 Thread-1 >>> 10

    结论

    在使用锁的时候要注意锁住的对象是谁,是否发生过改变。

  • 相关阅读:
    年轻人的第一个 Spring Boot 应用,太爽了!
    面试问我 Java 逃逸分析,瞬间被秒杀了。。
    Spring Boot 配置文件 bootstrap vs application 到底有什么区别?
    坑爹的 Java 可变参数,把我整得够惨。。
    6月来了,Java还是第一!
    Eclipse 最常用的 10 组快捷键,个个牛逼!
    Spring Cloud Eureka 自我保护机制实战分析
    今天是 Java 诞生日,Java 24 岁了!
    厉害了,Dubbo 正式毕业!
    Spring Boot 2.1.5 正式发布,1.5.x 即将结束使命!
  • 原文地址:https://www.cnblogs.com/shamao/p/10869679.html
Copyright © 2011-2022 走看看