IllegalMonitorStateException в Java

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.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, трябва да правим всяко обаждане, за да изчакаме () , известим () и известимВсички () методи в синхронизиран блок. Имайки предвид това, нека видим как трябва да изглежда правилното изпълнение на класа 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(); } } }

Обърнете внимание, че използваме синхронизирания блок на същия екземпляр от данни, който по-късно извикваме неговия метод 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.