IllegalMonitorStateException in Java

1. Panoramica

In questo breve tutorial, impareremo a conoscere java.lang.IllegalMonitorStateException.

Creeremo una semplice applicazione mittente-destinatario che genera questa eccezione. Quindi, discuteremo i possibili modi per prevenirlo. Infine, mostreremo come implementare correttamente queste classi mittente e destinatario.

2. Quando viene lanciato?

L'IllegalMonitorStateException è legato alla programmazione multithreading in Java. Se abbiamo un monitor su cui vogliamo sincronizzarci, questa eccezione viene lanciata per indicare che un thread ha provato ad aspettare o per notificare altri thread in attesa su quel monitor, senza possederlo. In parole più semplici, otterremo questa eccezione se chiamiamo uno dei metodi wait () , notify () o notifyAll () della classe Object al di fuori di un blocco sincronizzato .

Creiamo ora un esempio che genera un'eccezione IllegalMonitorStateException . Per questo, useremo entrambi i metodi wait () e notifyAll () per sincronizzare lo scambio di dati tra un mittente e un destinatario.

In primo luogo, diamo un'occhiata alla classe Data che contiene il messaggio che invieremo:

public class Data { private String message; public void send(String message) { this.message = message; } public String receive() { return message; } }

In secondo luogo, creiamo la classe sender che genera un'eccezione IllegalMonitorStateException quando invocata . A questo scopo, chiameremo il metodo notifyAll () senza avvolgerlo in un blocco sincronizzato :

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

Il ricevitore lancerà anche un'eccezione IllegalMonitorStateException. Analogamente all'esempio precedente, effettueremo una chiamata al metodo wait () all'esterno di un blocco sincronizzato :

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

Infine, istanziamo entrambe le classi e inviamo un messaggio tra di loro:

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

Quando cerchiamo di eseguire questo pezzo di codice, riceveremo un IllegalMonitorStateException da entrambi UnsynchronizedReceiver e UnsynchronizedSender classi:

[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. Come correggerlo

Per sbarazzarci dell'eccezione IllegalMonitorStateException, dobbiamo effettuare ogni chiamata ai metodi wait () , notify () e notifyAll () all'interno di un blocco sincronizzato . Con questo in mente, vediamo come dovrebbe apparire la corretta implementazione della classe 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(); } } }

Nota che stiamo usando il blocco sincronizzato sulla stessa istanza di Data che chiameremo in seguito il suo metodo notifyAll () .

Ripariamo il ricevitore allo stesso modo:

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

Se creiamo di nuovo entrambe le classi e proviamo a inviare lo stesso messaggio tra di loro, tutto funziona bene e non viene generata alcuna eccezione.

4. Conclusione

In questo articolo abbiamo appreso cosa causa IllegalMonitorStateException e come prevenirlo.

Come sempre, il codice è disponibile su GitHub.