1. Información general
En este breve tutorial, aprenderemos sobre java.lang.IllegalMonitorStateException.
Crearemos una aplicación de remitente-receptor simple que arroja esta excepción. Luego, discutiremos las posibles formas de prevenirlo. Finalmente, mostraremos cómo implementar estas clases de remitente y receptor correctamente.
2. ¿Cuándo se lanza?
La IllegalMonitorStateException está relacionada con la programación de subprocesos múltiples en Java. Si tenemos un monitor en el que queremos sincronizar, se lanza esta excepción para indicar que un hilo intentó esperar o notificar a otros hilos esperando en ese monitor, sin poseerlo. En palabras más simples, obtendremos esta excepción si llamamos a uno de los métodos wait () , notificar () o notificar a todos () de la clase Object fuera de un bloque sincronizado .
Construyamos ahora un ejemplo que arroja una IllegalMonitorStateException . Para esto, usaremos los métodos wait () y notifyAll () para sincronizar el intercambio de datos entre un remitente y un receptor.
En primer lugar, veamos la clase de datos que contiene el mensaje que vamos a enviar:
public class Data { private String message; public void send(String message) { this.message = message; } public String receive() { return message; } }
En segundo lugar, creemos la clase de remitente que arroja una IllegalMonitorStateException cuando se invoca . Para este propósito, llamaremos al método notifyAll () sin envolverlo en un bloque sincronizado :
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(); } } }
El receptor también va a lanzar una IllegalMonitorStateException. De manera similar al ejemplo anterior, realizaremos una llamada al método wait () fuera de un bloque sincronizado :
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; } }
Finalmente, creemos una instancia de ambas clases y enviemos un mensaje entre ellas:
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); }
Cuando tratamos de ejecutar esta pieza de código, vamos a recibir un IllegalMonitorStateException de ambos UnsynchronizedReceiver y UnsynchronizedSender clases:
[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. Cómo solucionarlo
Para deshacernos de la IllegalMonitorStateException, necesitamos hacer cada llamada a los métodos wait () , notificar () y notificar a todos () dentro de un bloque sincronizado . Con esto en mente, veamos cómo debería verse la implementación correcta de la clase 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(); } } }
Tenga en cuenta que estamos usando el bloque sincronizado en la misma instancia de datos que luego llamamos su método notifyAll () .
Arreglemos el receptor de la misma manera:
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 volvemos a crear ambas clases e intentamos enviar el mismo mensaje entre ellas, todo funciona bien y no se lanza ninguna excepción.
4. Conclusión
En este artículo, aprendimos qué causa IllegalMonitorStateException y cómo prevenirlo.
Como siempre, el código está disponible en GitHub.