IllegalMonitorStateException en Java

1. Vue d'ensemble

Dans ce court didacticiel, nous découvrirons java.lang.IllegalMonitorStateException.

Nous allons créer une simple application émetteur-récepteur qui lève cette exception. Ensuite, nous discuterons des moyens possibles de l'empêcher. Enfin, nous montrerons comment implémenter correctement ces classes d'expéditeur et de récepteur.

2. Quand est-il lancé?

L' exception IllegalMonitorStateException est liée à la programmation multithreading en Java. Si nous avons un moniteur sur lequel nous voulons synchroniser, cette exception est lancée pour indiquer qu'un thread a essayé d'attendre ou pour notifier d'autres threads en attente sur ce moniteur, sans le posséder. En termes plus simples, nous obtiendrons cette exception si nous appelons l'une des méthodes wait () , notify () ou notifyAll () de la classe Object en dehors d'un bloc synchronisé .

Construisons maintenant un exemple qui lève une IllegalMonitorStateException . Pour cela, nous utiliserons les deux méthodes wait () et notifyAll () pour synchroniser l'échange de données entre un expéditeur et un destinataire.

Tout d'abord, regardons la classe Data qui contient le message que nous allons envoyer:

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

Deuxièmement, créons la classe d'expéditeur qui lève une exception IllegalMonitorStateException lorsqu'elle est appelée . Pour cela, nous appellerons la méthode notifyAll () sans l'envelopper dans un bloc synchronisé :

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

Le récepteur va également lancer une exception IllegalMonitorStateException. Comme dans l'exemple précédent, nous allons appeler la méthode wait () en dehors d'un bloc synchronisé :

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

Enfin, instancions les deux classes et envoyons un message entre elles:

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

Lorsque nous essayons d'exécuter ce morceau de code, nous recevrons une IllegalMonitorStateException des classes UnsynchronizedReceiver et 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. Comment y remédier

Pour nous débarrasser de l' exception IllegalMonitorStateException, nous devons faire chaque appel aux méthodes wait () , notify () et notifyAll () dans un bloc synchronisé . Dans cet esprit, voyons à quoi devrait ressembler l'implémentation correcte de la 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(); } } }

Notez que nous utilisons le bloc synchronisé sur la même instance de données que nous appelons plus tard sa méthode notifyAll () .

Fixons le récepteur de la même manière:

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

Si nous créons à nouveau les deux classes et essayons d'envoyer le même message entre elles, tout fonctionne bien et aucune exception n'est lancée.

4. Conclusion

Dans cet article, nous avons appris ce qui cause IllegalMonitorStateException et comment l'empêcher.

Comme toujours, le code est disponible sur sur GitHub.