Рубрики
Без рубрики

Исключение IllegalMonitorStateException в Java

Если вы хотите узнать, почему ваш код продолжает выдавать это исключение, эта статья для вас.

Автор оригинала: baeldung.

1. Обзор

В этом коротком уроке мы узнаем о java.lang.Исключение IllegalMonitorStateException.

Мы создадим простое приложение отправителя-получателя, которое вызовет это исключение. Затем мы обсудим возможные способы предотвращения этого. Наконец, мы покажем, как правильно реализовать эти классы отправителя и получателя.

2. Когда Он Выбрасывается?

Исключение IllegalMonitorStateException связано с многопоточным программированием на Java. Если у нас есть монитор , на котором мы хотим синхронизировать, это исключение выдается, чтобы указать, что поток пытался подождать или уведомить другие потоки, ожидающие на этом мониторе, не владея им. Проще говоря, мы получим это исключение, если вызовем один из методов wait() , notify(), или notifyAll() класса Object за пределами синхронизированного блока.

Давайте теперь построим пример, который вызывает исключение IllegalMonitorStateException . Для этого мы будем использовать оба метода wait() и notifyAll() для синхронизации обмена данными между отправителем и получателем.

Во-первых, давайте посмотрим на класс Data , который содержит сообщение, которое мы собираемся отправить:

public class Data {
    private String message;

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

    public String receive() {
        return message;
    }
}

Во-вторых, давайте создадим класс sender, который вызывает исключение IllegalMonitorStateException при вызове . Для этой цели мы вызовем метод notify All() , не оборачивая его в синхронизированный блок:

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();
        }
    }
}

Получатель также собирается выдать исключение 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;
    }
}

Наконец, давайте создадим экземпляры обоих классов и отправим сообщение между ними:

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);
}

Когда мы попытаемся запустить этот фрагмент кода, мы получим исключение IllegalMonitorStateException от обоих классов UnsynchronizedReceiver и UnsynchronizedSender :

[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. Как это исправить

Чтобы избавиться от исключения IllegalMonitorStateException, нам нужно выполнять каждый вызов методов 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();
        }
    }
}

Обратите внимание, что мы используем блок synchronized на том же экземпляре Data , который мы позже вызовем его метод notifyAll () .

Давайте исправим Приемник таким же образом:

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;
    }
}

Если мы снова создадим оба класса и попытаемся отправить одно и то же сообщение между ними, все будет работать хорошо, и никаких исключений не будет.

4. Заключение

В этой статье мы узнали, что вызывает IllegalMonitorStateException и как его предотвратить.

Как всегда, код доступен на GitHub .