java notifi与wait的使用在下面代码中为什么会报错

public class Test {
static Object obj=new Object();
public static void main(String[] args) {
new Thread(){
public void run(){
try {
Thread.sleep(10000);
System.out.println("线程即将执行...");
obj.notify();
} catch (InterruptedException e) {
}
}
}.start();

synchronized (obj) {
try {
obj.wait();
System.out.println("我出来了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
--------------------------------》

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.ldc.test.Test$1.run(Test.java:16)

首先我们来看一下文档中的相关描述

引起java.lang.IllegalMonitorStateException的原因是当前线程不是此对象监视器的所有者,问题来了,什么样的线程才是对象监视器的所有者?

你的代码中定义的obj就是一个对象监视器,我想这一点你应该明白。我们说进入到该同步代码块中的线程将获得该对象监视器(也就是该对象监视器的所有者了),如果这个线程一直占有该监视器,那么其他线程将不能获得该监视器,以此来达到同步的目的。

再来看你的代码,里面一共有两个线程,一个是主线程,一个是你通过new thread(...)弄出来的线程,下面用新线程代表这个线程。使用你定义的obj作为监视器的同步块就只有下面这段:

synchronized (obj) {
try {
    obj.wait();
    System.out.println("我出来了...");
} catch (InterruptedException e) {
    e.printStackTrace();
}
}

先说新线程,一开始就要睡10秒对吧?在这10秒内,主线程进入上面这个同步块,获得obj这个监视器,然后obj.wait();导致主线程暂停,同时放开对obj的锁定(由于主线程放开了对obj的锁定,这个时候其他线程是可以获得obj并进入上面这个同步块的哦),现在主线程就在哪wait了对吧。10秒过后新线程睡醒了,输出了一句话:“线程即将执行...”,然后就是下面这行代码了:

obj.notify();

新线程想去唤醒obj上等待的线程,文档说的很清楚,如果不是对象监视器的所有者就将抛出java.lang.IllegalMonitorStateException异常,此时的新线程是obj的所有者吗?上面我说过了你得进入拿到监视器进入同步代码块才是监视器的所有者啊,很明显这个新线程不是obj的所有者嘛,抛出这个异常现在可以理解?

现在就想让这个新线程去唤醒在obj上等待的主线程怎么办,要解决一个问题,让这个新线程称为obj的所有者,说过两遍了,进入同步监视器对应的代码块就是这个监视器的所有者了,具体怎么办呢,把

obj.notify();

放到由obj作为监视器的同步代码块不就行了吗?将你的代码改成下面这样:

public class Test {
static Object obj = new Object();

public static void main(String[] args) {
new Thread() {
public void run() {
try {
Thread.sleep(10000);
System.out.println("线程即将执行...");
synchronized (obj) {
obj.notify();
}
} catch (InterruptedException e) {
}
}
}.start();

synchronized (obj) {
try {
obj.wait();
System.out.println("我出来了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

就加了两行代码(其中一行还只是个括号。。。),和之前一样的分析,新线程睡了,主线程拿到obj这个监视器进入同步块,然后obj.wait();导致主线程等待,并且放开了对obj的锁定,10秒过后,新线程醒了,由于主线程放开了对obj的锁定,下面这个地方

synchronized (obj) {
obj.notify();
}

新线程获得obj监视器,也就是成为了obj的所有者,然后取唤醒在obj上等待的线程(主线程),这个是没有什么问题的,唤醒之后,新线程就完成任务了从同步代码块中出去了,并放开了对监视器的锁定,主线程这时可获得该监视器,然后继续执行,这个程序将正常运行。

为了验证主线程必须等到新线程放开监视器后才能获得监视器并继续执行,继续修改上面代码:

public class Test {
static Object obj = new Object();

public static void main(String[] args) {
new Thread() {
public void run() {
try {
Thread.sleep(10000);
System.out.println("线程即将执行...");
synchronized (obj) {
obj.notify();
Thread.sleep(10000);
}
} catch (InterruptedException e) {
}
}
}.start();

synchronized (obj) {
try {
obj.wait();
System.out.println("我出来了...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

让新线程唤醒主线程后再睡10秒,你会发现,必须等到10过后主线程才继续执行。换句话说在输出“线程即将执行...”之后又过了10秒才输出“我出来了...”。


菜鸟之见,大神勿喷。有什么错误没请指正。

温馨提示:答案为网友推荐,仅供参考