Java问题记录——IllegalMonitorStateException

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

部分内容来自以下博客:

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

锁对象发生了改变

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

代码分析

代码如下:

 public class Demo {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
Thread thread1 = new Thread(demoThread);
Thread thread2 = new Thread(demoThread);
thread1.start();
thread2.start();
}
} class DemoThread implements Runnable {
private Integer num = 1; @Override
public void run() {
while (true) {
synchronized (num) {
num.notify();
if (num <= 10) {
System.out.println(Thread.currentThread().getName() + " >>> " + num++);
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

运行结果如下:

 Thread-0 >>> 1
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.iyao.ide.engine.task.DemoThread.run(Demo.java:22)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.iyao.ide.engine.task.DemoThread.run(Demo.java:26)
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类型,可以保证对它的修改不会产生新的对象。

解决方案

代码修改后如下:

 public class Demo {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
Thread thread1 = new Thread(demoThread);
Thread thread2 = new Thread(demoThread);
thread1.start();
thread2.start();
}
} class DemoThread implements Runnable {
private AtomicInteger num = new AtomicInteger(1); @Override
public void run() {
while (true) {
synchronized (num) {
num.notify();
if (num.intValue() <= 10) {
System.out.println(Thread.currentThread().getName() + " >>> " + num.getAndAdd(1));
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

运行结果如下:

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

结论

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

Java问题记录——IllegalMonitorStateException的更多相关文章

  1. Java 日志记录规则

    Java 日志记录规则 规则一:日志是面向读者的 我们不应该让无价值的信息使日志文件变得乱糟糟,比如说完整打印所有的实体字段. 通常,实体名字和其逻辑关键字足以识别在表格中的一条记录了. 规则二:匹配 ...

  2. 补充Java面试记录

    补充Java面试记录 背景:这两天面试遇到的部分问题都分散在了前面两篇文摘中,这里再做一些其他的记录,以备不时之需! 一.谈谈你对SpringBoot的理解? SpringBoot简介:SpringB ...

  3. Java问题记录——循环里的二次判断与状态更新

    Java问题记录——循环里的二次判断与状态更新 摘要:本文主要记录了在循环操作时可能出现的问题. 问题重现 在使用循环结构时,如果使用了定时任务,或者代码会多次调用循环结构,可能会导致有些对象会被循环 ...

  4. Java问题记录——OutOfMemoryError

    Java问题记录——OutOfMemoryError 摘要:本文主要分析了OutOfMemoryError的产生原因. 没有分页导致占用大量内存 查看进程 使用 jps 命令查看当前运行的Java进程 ...

  5. SLF4J (The Simple Logging Facade for Java)使用记录

    SLF4J (The Simple Logging Facade for Java)使用记录 官网 http://www.slf4j.org/ 参考资料 官方文档 什么是 SLF4J? 官网: The ...

  6. 【Java】记录一次代码优化

    前不久的项目时间紧张,为了尽快完成原型开发,写了一段效率相当低的代码. 最近几天闲下来,主动把之前的代码优化了一下:)   标签:Java.Mybatis.MySQL 概况:本地系统从另外一个系统得到 ...

  7. 普华永道高级JAVA面试记录

    最近在考虑换个工作 原因?咱能不逗吗? 一面感觉发挥不错  二面之后累觉不爱  基本上浪费了半天的工资(好多钱啊~~~) PWD上海地址在浦东软件园  工作环境说实话没我现在工作的环境好,不过里面的人 ...

  8. Java学习笔记(十九)——Java 日志记录 AND log4j

    [前面的话] 学习的进度应该稍微在快一点. Java日志到了必须学习怎么使用的时候了,因为在项目中要进行使用.基础性文章,选择性阅读. [结构] java日志对调试,记录运行,问题定位都起到了很重要的 ...

  9. Java日志记录的事儿

    一.java日志组件 1.common-logging common-logging是apache提供的一个通用的日志接口.用户可以自由选择第三方的日志组件作为具体实现,像log4j,或者jdk自带的 ...

随机推荐

  1. ThreadLocal(线程本地存储)

    1. ThreadLocal,即线程本地变量或线程本地存储. threadlocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的 ...

  2. newSingleThreadScheduledExecutor连续关闭造成 java.util.concurrent.RejectedExecutionException

    Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util ...

  3. [20190515]热备份模式与rman冲突.txt

    [20190515]热备份模式与rman冲突.txt --//别人的系统做dg时打开热备份模式,忘记关闭,做rman备份时报错.做一个记录.--//实际上也怪自己,实施时没有讲清楚.通过例子说明: 1 ...

  4. MySQL数据篇(五)--SQL对数据进行按月统计,或对数据进行按星期统计

    对于所有的需求,当你不知道怎么处理的时候,你就先用最简单的方法,或者说的明白一点,用最原始的方法,先实现业务需求再说. 一.对提现队列数据表“ims_checkout_task”进行汇总统计,按月汇总 ...

  5. Jmeter之命令行生成HTML报告

    其实每次使用jemter.bat文件启动JMeter时,命令行窗口都会提示我们不要使用GUI窗口进行测试,除非是进行调试脚本 使用命令行生成结果也很测试报告也很简单 jmeter -n -t [jmx ...

  6. Shell脚本(1)

    在创建Shell脚本时,必须在文件的第一行指定要使用的shell.格式为:#!/bin/bash 除了第一行外,在shell脚本中井号(#)用作注释行 若出现错误:command not found ...

  7. fiddler---Fiddler弱网测试

    无论是做web端还是app端的测试,我们都需要对弱网进行测试,对于弱网方法有很多种,如:Fiddler模拟弱网,控制电脑的网速等,今天介绍下Fiddler如何进行测试弱网 什么是弱网 弱网看字面意思就 ...

  8. 03-flex-wrap是否换行

     flex-wrap:运用到父元素上 结合 display: flex; flex-wrap: wrap; 换行 flex-wrap: nowrap; 不换行 #main { width: 300px ...

  9. 【cf570】D. Tree Requests(dsu on tree)

    传送门 题意: 给出一个以\(1\)为根的有根树.之后有\(m\)个询问,每个询问为\(v_i,h_i\),需要回答以\(v_i\)为根的子树中,深度为\(h_i\)的那些结点所代表的字符能否构成回文 ...

  10. 201271050130-滕江南-《面向对象程序设计(java)》第十二周学习总结

    201271050130-滕江南-<面向对象程序设计(java)>第十二周学习总结 项       目 内      容 这个作业属于哪个课程 https://www.cnblogs.co ...