【问题标题】:IllegalMonitorStateException calling wait() and notify()IllegalMonitorStateException 调用 wait() 和 notify()
【发布时间】:2014-06-01 03:49:11
【问题描述】:

我看了生产者和消费者的例子,稍微改了一下。 Pro() 将打印“第一”,而 con() 将打印“第二”。我希望每个“第二”出现在“第一”之后。

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)

【问题讨论】:

  • @Java42 谢谢。我知道应该有“同步”,并且我已经在我的方法中添加了“同步”关键词。为什么还是错了?
  • 更新您的问题,不要说“它不起作用”,而是说“我收到错误:”
  • @Sentimental 你的run() 方法应该同步,而不是pro()con() 方法。

标签: multithreading concurrency wait notify


【解决方案1】:

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

第一个是明显的IllegalMonitorStateException。当调用线程不持有该对象的监视器锁时,在对象上调用 waitnotify 时会发生此异常。尽管调用在语法上发生在 synchronized 方法中,但对 waitnotify 的实际调用发生在不同的线程上并在不同的对象上进行操作。

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

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

您需要做的是重新排列代码,以便synchronized 块出现在执行实际同步的线程中。然后,您要确保synchronizedwaitnotify 操作都在同一个对象上。也许最好的方法是将test 实例存储在静态字段中(例如,使m 成为静态字段而不是局部变量)。 run 方法是 Runnable 实例的成员,因此您不能只使这些方法同步。相反,您必须使用 synchronized 块并传递要同步的实例。您还可以从procon 方法中删除synchronized 关键字,因为这些方法实际上不需要同步。生成的代码将如下所示:

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 也是行不通的。

有关如何正确使用 waitnotify 的完整说明,请参阅 Goetz,Java 并发实践,第 14 章。

【讨论】:

【解决方案2】:

为了能够(没有IllegalMonitorStateException)调用某个对象的wait()notify() 方法,线程必须首先获取对象监视器。这意味着这些方法应该在 synchronized 块中由与 wait() 相同的对象调用,或者在 synchronized 方法中调用,您可以在其中调用 this.wait()

现在让我们看看你的代码:

@Override
public void run() {
    while (true) {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Second!");              
        notify();                   
    }
}

wait()notify() 用于匿名 Runnable 实例。这可能是由于对等待/通知机制的误解而导致的错误。尝试阅读this article 了解更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 2012-05-10
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多