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.