publicclassMyTest {
publicstaticvoidmain(String[] args)throwsException {
newTestThread().start();
newTestThread().start();
Thread.sleep(1000);
System.out.println("Doing something...");
synchronized(lock) {
lock =false;// 语句1
lock.notifyAll();// 语句2
}
}
staticvolatileBoolean lock =true;
}
classTestThreadextendsThread {
@Override
publicvoidrun() {
synchronized(MyTest.lock) {
while(MyTest.lock) {
try{
MyTest.lock.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getId());
}
}
}public class MyTest {
public static void main(String[] args) throws Exception {
new TestThread().start();
new TestThread().start();
Thread.sleep(1000);
System.out.println("Doing something...");
synchronized (lock) {
lock = false; // 语句1
lock.notifyAll(); // 语句2
}
}
static volatile Boolean lock = true;
}
class TestThread extends Thread {
@Override
public void run() {
synchronized (MyTest.lock) {
while (MyTest.lock) {
try {
MyTest.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getId());
}
}
}输出结果为
Doing something...
Exception in thread"main"java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at MyTest.test(MyTest.java:13)
at MyTest.main(MyTest.java:3
如果把语句1和语句2调换一下就正常了。 大家给诊断一下,到底是为什么?
讨论过程请大家自行参考原始的帖子和我的整理帖子,这里只给出最终的分析结果
分析:当作为同步锁的对象发生改变时,比如换了一个新的对象,那么如果在新的对象上调用同步的wait等方法,由于并没有同步这个对象,而是同步的改变以前的那个,就会报出如上的异常。 我们来看代码
synchronized(lock) {
lock =false;// 语句1
lock.notifyAll();// 语句2
}synchronized (lock) {
lock = false; // 语句1
lock.notifyAll(); // 语句2
}
语句1那里修改了lock的对象指向,结果造成下面的一句notifyAll使用了一个并没有synchronized的对象,随意报出了异常。
解决方法
方案1,使用Boolean的特殊性 由于 lock=false;的特殊性,分析Boolean的源代码发现
publicstaticfinalBoolean TRUE =newBoolean(true);
publicstaticfinalBoolean FALSE =newBoolean(false);
publicstaticBoolean valueOf(booleanb) {
return(b ? TRUE : FALSE);
}
//发现其内部对于自动打包,拆包只使用了2个固定的对象。所以可以用
synchronized(lock) {
lock =false;// 语句1
Boolean.TRUE.notifyAll();// 语句2
}
// 直接使用那个TRUE就行了。public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
//发现其内部对于自动打包,拆包只使用了2个固定的对象。所以可以用
synchronized (lock) {
lock = false; // 语句1
Boolean.TRUE.notifyAll(); // 语句2
}
// 直接使用那个TRUE就行了。
方法2:使用一个参数可变对象,而不是不可变的,
比如
class MyLock {
boolean lock = true;
}
staticvolatileMyLock lock =newMyLock();static volatile MyLock lock = new MyLock();
// 然后再代码里面用
lock.lock=false;// 进行标志的变更和判断就可以了
结论:
同步锁最好单独使用,如果锁自身附带了其它作用,应使用一个可变的对象 推荐
staticvolatileMyLock lock =newMyLock();static volatile MyLock lock = new MyLock();应该写成
finalstaticvolatileMyLock lock =newMyLock();final static volatile MyLock lock = new MyLock();
本文分析了一段Java代码中出现的IllegalMonitorStateException异常原因,并提供了两种解决方案:一是利用Boolean对象的特殊性,二是使用可变对象作为锁。通过这两种方法避免了因同步锁对象在通知线程前被更改而导致的问题。
316

被折叠的 条评论
为什么被折叠?



