IllegalMonitorStateException in Java – Java中的IllegalMonitorStateException

最后修改: 2020年 9月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this short tutorial, we’ll learn about java.lang.IllegalMonitorStateException. 

在这个简短的教程中,我们将学习java.lang.IllegalMonitorStateException。

We’ll create a simple sender-receiver application that throws this exception. Then, we’ll discuss possible ways of preventing it.  Finally, we’ll show how to implement these sender and receiver classes correctly.

我们将创建一个简单的抛出这种异常的发送方-接收方应用程序。然后,我们将讨论防止它的可能方法。 最后,我们将展示如何正确实现这些发送方和接收方类。

2. When Is It Thrown?

2.何时抛出?

The IllegalMonitorStateException is related to multithreading programming in Java. If we have a monitor we want to synchronize on, this exception is thrown to indicate that a thread tried to wait or to notify other threads waiting on that monitor, without owning it. In simpler words, we’ll get this exception if we call one of the wait(), notify(), or notifyAll() methods of the Object class outside of a synchronized block.

IllegalMonitorStateException与Java的多线程编程有关。如果我们有一个monitor,我们想在上面进行同步,这个异常被抛出,以表明一个线程试图等待或通知在该监视器上等待的其他线程,而不拥有它。更简单地说,如果我们在同步块之外调用Object类的wait()notify()notifyAll()方法,我们会得到这个异常。

Let’s now build an example that throws an IllegalMonitorStateException. For this, we’ll use both wait() and notifyAll() methods to synchronize the data exchange between a sender and a receiver.

现在让我们构建一个抛出IllegalMonitorStateException的例子。为此,我们将使用wait()notifyAll()方法来同步发送器和接收器之间的数据交换。

Firstly, let’s look at the Data class that holds the message we’re going to send:

首先,让我们看一下持有我们要发送的消息的Data类。

public class Data {
    private String message;

    public void send(String message) {
        this.message = message;
    }

    public String receive() {
        return message;
    }
}

Secondly, let’s create the sender class that throws an IllegalMonitorStateException when invoked. For this purpose, we’ll call the notifyAll() method without wrapping it in a synchronized block:

其次,让我们创建一个发送器类,在调用时抛出一个IllegalMonitorStateException 为此,我们将调用notifyAll()方法,而不把它包装在同步块中。

class UnsynchronizedSender implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(UnsychronizedSender.class);
    private final Data data;

    public UnsynchronizedSender(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);

            data.send("test");

            data.notifyAll();
        } catch (InterruptedException e) {
            log.error("thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    }
}

The receiver is also going to throw an IllegalMonitorStateException. Similarly to the previous example, we’ll make a call to the wait() method outside a synchronized block:

接收方也将抛出一个IllegalMonitorStateException。与之前的例子类似,我们将在同步块之外调用wait()方法。

public class UnsynchronizedReceiver implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(UnsynchronizedReceiver.class);
    private final Data data;
    private String message;

    public UnsynchronizedReceiver(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        try {
            data.wait();
            this.message = data.receive();
        } catch (InterruptedException e) {
            log.error("thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    }

    public String getMessage() {
        return message;
    }
}

Finally, let’s instantiate both classes and send a message between them:

最后,让我们实例化这两个类并在它们之间发送一条消息。

public void sendData() {
    Data data = new Data();

    UnsynchronizedReceiver receiver = new UnsynchronizedReceiver(data);
    Thread receiverThread = new Thread(receiver, "receiver-thread");
    receiverThread.start();

    UnsynchronizedSender sender = new UnsynchronizedSender(data);
    Thread senderThread = new Thread(sender, "sender-thread");
    senderThread.start();

    senderThread.join(1000);
    receiverThread.join(1000);
}

When we try to run this piece of code, we’ll receive an IllegalMonitorStateException from both UnsynchronizedReceiver and UnsynchronizedSender classes:

当我们尝试运行这段代码时,我们将从UnsynchronizedReceiverUnsynchronizedSender类中收到一个IllegalMonitorStateException

[sender-thread] ERROR com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedSender - illegal monitor state exception occurred
java.lang.IllegalMonitorStateException: null
	at java.base/java.lang.Object.notifyAll(Native Method)
	at com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedSender.run(UnsynchronizedSender.java:15)
	at java.base/java.lang.Thread.run(Thread.java:844)

[receiver-thread] ERROR com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedReceiver - illegal monitor state exception occurred
java.lang.IllegalMonitorStateException: null
	at java.base/java.lang.Object.wait(Native Method)
	at java.base/java.lang.Object.wait(Object.java:328)
	at com.baeldung.exceptions.illegalmonitorstate.UnsynchronizedReceiver.run(UnsynchronizedReceiver.java:12)
	at java.base/java.lang.Thread.run(Thread.java:844)

3. How to Fix It

3.如何修复它

To get rid of the IllegalMonitorStateException, we need to make every call to wait(), notify(), and notifyAll() methods within a synchronized block. With this in mind, let’s see how the correct implementation of the Sender class should look like:

为了摆脱IllegalMonitorStateException,我们需要在synchronized块中对wait()notify()notifyAll()方法进行每次调用。考虑到这点,让我们看看Sender类的正确实现应该是什么样子。

class SynchronizedSender implements Runnable {
    private final Data data;

    public SynchronizedSender(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        synchronized (data) {
            data.send("test");

            data.notifyAll();
        }
    }
}

Note we’re using the synchronized block on the same Data instance we later call its notifyAll() method.

请注意,我们在同一个Data实例上使用了synchronized块,后来我们又调用其notifyAll()方法。

Let’s fix the Receiver in the same way:

让我们以同样的方式修复Receiver

class SynchronizedReceiver implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(SynchronizedReceiver.class);
    private final Data data;
    private String message;

    public SynchronizedReceiver(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        synchronized (data) {
            try {
                data.wait();
                this.message = data.receive();
            } catch (InterruptedException e) {
                log.error("thread was interrupted", e);
                Thread.currentThread().interrupt();
            }
        }
    }

    public String getMessage() {
        return message;
    }
}

If we again create both classes and try to send the same message between them, everything works well, and no exception is thrown.

如果我们再次创建这两个类,并尝试在它们之间发送相同的消息,一切都很顺利,没有抛出异常。

4. Conclusion

4.总结

In this article, we learned what causes IllegalMonitorStateException and how to prevent it.

在这篇文章中,我们了解了导致IllegalMonitorStateException的原因以及如何防止它。

As always, the code is available over on GitHub.

像往常一样,代码可在GitHub上获得