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;
}
}
Во-вторых, давайте создадим класс отправителя, который при вызове выдает исключение 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();
}
}
}
Получатель также будет генерировать исключение 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.foreach.exceptions.illegalmonitorstate.UnsynchronizedSender - illegal monitor state exception occurred
java.lang.IllegalMonitorStateException: null
at java.base/java.lang.Object.notifyAll(Native Method)
at com.foreach.exceptions.illegalmonitorstate.UnsynchronizedSender.run(UnsynchronizedSender.java:15)
at java.base/java.lang.Thread.run(Thread.java:844)
[receiver-thread] ERROR com.foreach.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.foreach.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()
.
Таким же образом починим 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;
}
}
Если мы снова создадим оба класса и попытаемся отправить одно и то же сообщение между ними, все будет работать хорошо, и никаких исключений не будет.
4. Вывод
В этой статье мы узнали, что вызывает исключение IllegalMonitorStateException
и как его предотвратить.
Как всегда, код доступен на GitHub .