调用wait()和notify()的IllegalMonitorStateException

时间:2014-06-01 03:49:11

标签: multithreading concurrency wait notify

我已经阅读了生产者和消费者的例子,我改变了一点。 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)

2 个答案:

答案 0 :(得分:4)

这里同时出现了几个问题。

第一个是显而易见的IllegalMonitorStateException。当调用线程不保持该对象的监视器锁定时,在对象上调用waitnotify时会发生此异常。即使调用语法在synchronized方法中发生,对waitnotify的实际调用也会发生在不同的线程上并对不同的对象进行操作。

发生的事情是您的procon方法在您的实例上同步。由于它们是从main方法调用的,因此主线程是采用监视器锁定的线程。但是这些方法只是启动其他线程然后释放锁。

waitnotify来电是隐式this.waitthis.notify,并且它们位于Runnable匿名内部类中,所以在这些情况下{{1} }}指的是this个实例,而不是已经同步的外部Runnable个实例。规则是在对象上调用test(或wait)的同一线程也必须在对象上保持监视器锁定。由于情况并非如此,因此抛出notify。另一个问题是,IllegalMonitorStateExceptionwait调用位于不同的 notify实例上。不同的线程必须在相同的对象上使用wait / notify才能进行通信。

您需要做的是重新排列代码,以便在执行实际同步的线程中出现Runnable块。然后,您要确保synchronizedsynchronizedwait操作都在同一个对象上。也许最好的方法是将notify实例存储在静态字段中(例如,make test是静态字段而不是局部变量)。 m方法是run个实例的成员,因此您无法使这些方法同步。相反,您必须使用Runnable块并传递要同步的实例。您还可以从synchronizedsynchronized方法中删除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也不行。

有关如何正确使用notifywait的完整说明,请参阅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以获取更多信息。