我已经阅读了生产者和消费者的例子,我改变了一点。 Pro()将打印“first”,con()将打印“second”。我希望每一个“秒”出现在“第一”之后。
public class test {
test() { }
synchronized void pro() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("First!");
notify();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
synchronized void con() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Second!");
notify();
}
}
}).start();
}
public static void main(String[] args) {
test m = new test();
m.pro();
m.con();
}
}
出现的错误是:
First!
Exception in thread "Thread-0" Exception in thread "Thread-1"
java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at test$1.run(test.java:12)
at java.lang.Thread.run(Unknown Source)
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:503)
at test$2.run(test.java:32)
at java.lang.Thread.run(Unknown Source)
答案 0 :(得分:4)
这里同时出现了几个问题。
第一个是显而易见的IllegalMonitorStateException
。当调用线程不保持该对象的监视器锁定时,在对象上调用wait
或notify
时会发生此异常。即使调用语法在synchronized
方法中发生,对wait
和notify
的实际调用也会发生在不同的线程上并对不同的对象进行操作。
发生的事情是您的pro
和con
方法在您的实例上同步。由于它们是从main
方法调用的,因此主线程是采用监视器锁定的线程。但是这些方法只是启动其他线程然后释放锁。
wait
和notify
来电是隐式this.wait
和this.notify
,并且它们位于Runnable
匿名内部类中,所以在这些情况下{{1} }}指的是this
个实例,而不是已经同步的外部Runnable
个实例。规则是在对象上调用test
(或wait
)的同一线程也必须在对象上保持监视器锁定。由于情况并非如此,因此抛出notify
。另一个问题是,IllegalMonitorStateException
和wait
调用位于不同的 notify
实例上。不同的线程必须在相同的对象上使用wait / notify才能进行通信。
您需要做的是重新排列代码,以便在执行实际同步的线程中出现Runnable
块。然后,您要确保synchronized
,synchronized
和wait
操作都在同一个对象上。也许最好的方法是将notify
实例存储在静态字段中(例如,make test
是静态字段而不是局部变量)。 m
方法是run
个实例的成员,因此您无法使这些方法同步。相反,您必须使用Runnable
块并传递要同步的实例。您还可以从synchronized
和synchronized
方法中删除pro
个关键字,因为这些方法实际上并不需要同步。生成的代码如下所示:
con
您必须执行与static test m = new test();
void pro() {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (m) {
...
m.notify();
...
m.wait();
...
}
}
}).start();
}
方法类似的操作。
现在,代码应该在不抛出con
的情况下运行。但这揭示了第二个问题:它可能不起作用。由于丢失唤醒问题,它可能会死锁。完整的解释超出了本答案的范围。简而言之,如果由IllegalMonitorStateException
启动的线程首先运行,则在没有线程等待时它可能会调用pro
。如果发生这种情况,则没有任然后该线程继续并调用notify
。接下来,wait
线程可能会启动,它所做的第一件事就是调用con
。现在,wait
中的两个主题都被阻止,wait
永远不会发生。
解决此问题的方法是根据对象的状态进行等待和通知。如果不检查对象的状态就调用notify
,在不更改对象状态的情况下调用wait
也不行。
有关如何正确使用notify
和wait
的完整说明,请参阅Goetz,实践中的Java并发,第14章。
答案 1 :(得分:0)
为了能够(没有IllegalMonitorStateException
)调用某个对象的wait()
和notify()
方法,线程必须首先获取对象监视器。这意味着应该在您synchronized
所使用的同一对象的wait()
块中调用此方法,或者在synchronized
方法中调用此方法,您可以在其中调用this.wait()
。< / p>
现在让我们来看看您的代码:
@Override
public void run() {
while (true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Second!");
notify();
}
}
对于匿名wait()
实例,您notify()
和Runnable
。这可能是由于对等待/通知机制的误解造成的错误。请尝试阅读this article以获取更多信息。