IllegalMonitorStateException en la llamada wait ()

162

Estoy usando multi-threading en java para mi programa. He ejecutado el hilo con éxito, pero cuando lo estoy usando Thread.wait(), está tirando java.lang.IllegalMonitorStateException. ¿Cómo puedo hacer que un hilo espere hasta que sea notificado?

prakash.panjwani
fuente
2
Thread.wait () no existe, podría ser this.wait ()
Premraj

Respuestas:

175

Debes estar en un synchronizedbloque paraObject.wait() poder trabajar.

Además, recomiendo mirar los paquetes de concurrencia en lugar de los paquetes de subprocesos de la vieja escuela. Son más seguros y más fáciles de trabajar .

Feliz codificación

EDITAR

Supuse que querías decir Object.wait()que tu excepción es lo que sucede cuando intentas obtener acceso sin mantener el bloqueo de objetos.

reccles
fuente
1
buena atrapada. Supuse que se refería a Object.wait () y llamé desde un hilo
recicle el
2
Un bloque sincronizado en el objeto que está esperando. ¿Te importaría editar esta respuesta para que quede un poco más claro? Gracias.
Gris
55

waitse define en Object, y no lo es Thread. El monitor encendido Threades un poco impredecible.

Aunque todos los objetos Java tienen monitores, generalmente es mejor tener un bloqueo dedicado:

private final Object lock = new Object();

Puede obtener diagnósticos un poco más fáciles de leer, a un bajo costo de memoria (aproximadamente 2K por proceso) utilizando una clase con nombre:

private static final class Lock { }
private final Object lock = new Lock();

Para / waito un objeto, debe mantener el bloqueo con la declaración. Además, necesitará un bucle para verificar la condición de activación (busque un buen texto sobre subprocesos para explicar por qué).notifynotifyAllsynchronizedwhile

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

Notificar:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Vale la pena entender tanto el lenguaje Java como los java.util.concurrent.locksbloqueos (y java.util.concurrent.atomic) al entrar en subprocesos múltiples. Pero use java.util.concurrentestructuras de datos siempre que pueda.

Tom Hawtin - tackline
fuente
55
Nunca he entendido cómo funciona esto, dado que la espera y la notificación están ambas en bloques sincronizados en el mismo objeto (bloqueo). Dado que el hilo de espera está en el bloque, ¿no debería eso hacer que el hilo de notificación se bloquee en la línea "sincronizada (bloqueo)"?
Brent212
66
@ Brent212 Para cualquier otro método que no sea waitsí, nunca lo harías notify. Sin embargo, en los documentos de API para Object.wait"El subproceso libera la propiedad de este monitor". Entonces, mientras está dentro wait, es como si estuviera fuera de los synchronizedbloques circundantes (para el mismo objeto, puede haber múltiples synchronizedbloques en el mismo objeto).
Tom Hawtin - tackline
24

Sé que este hilo tiene casi 2 años, pero aún necesito cerrar esto ya que también vine a esta sesión de preguntas y respuestas con el mismo problema ...

Lea esta definición de ilegalMonitorException una y otra vez ...

IllegalMonitorException se genera para indicar que un subproceso ha intentado esperar en el monitor de un objeto o para notificar a otros subprocesos que esperan en el monitor de un objeto sin poseer el monitor especificado.

Esta línea una y otra vez dice, IllegalMonitorException llega cuando ocurre una de las 2 situaciones ...

1> espera en el monitor de un objeto sin tener el monitor especificado.

2> notifique a otros hilos que esperan en el monitor de un objeto sin poseer el monitor especificado.

Algunos podrían haber obtenido sus respuestas ... quien no, entonces verifique 2 declaraciones ...

sincronizado (objeto)

object.wait ()

Si ambos objetos son iguales ... entonces no puede venir ninguna excepción de monitor ilegal.

Ahora lea nuevamente la definición IllegalMonitorException y no la olvidará nuevamente ...

Mina
fuente
En realidad, eso no funciona. Lo he intentado Creo un Runnable, lo bloqueo (usando el bloque sincronizado) y dentro de ese bloque ejecuto Runnable en el hilo UI (Android) y luego hago myRunnable.wait (), y todavía obtengo la excepción.
Ted
Excelente explicación !! Estaba haciendo wait () sin especificar el objeto, por lo que tomó la instancia y sincronicé con otro objeto. ¡Ahora estoy usando otherObject.wait () y funciona!
Fersca
6

Según sus comentarios, parece que está haciendo algo como esto:

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

Hay tres problemas

  1. Como han dicho otros, obj.wait()solo se puede invocar si el subproceso actual contiene el bloqueo / mutex primitivo para obj. Si el hilo actual no mantiene el bloqueo, obtienes la excepción que estás viendo.

  2. La thread.wait()llamada no hace lo que parece que espera que haga. Específicamente, thread.wait() no hace que el hilo nominado espere. Más bien hace que el hilo actual espere hasta que otro hilo llame thread.notify()o thread.notifyAll().

    En realidad, no existe una forma segura de forzar una Threadpausa de una instancia si no lo desea. (Lo más cercano que Java tiene a esto es el obsoletoThread.suspend() método , pero ese método es intrínsecamente inseguro, como se explica en el Javadoc).

    Si desea que el recién iniciado se Threaddetenga, la mejor manera de hacerlo es crear una CountdownLatchinstancia y hacer que la llamada de subproceso await()en el pestillo se detenga. El hilo principal luego llamaría countDown()al pestillo para permitir que el hilo en pausa continúe.

  3. Ortogonal a los puntos anteriores, el uso de un Threadobjeto como bloqueo / mutex puede causar problemas. Por ejemplo, el javadoc para Thread::joindice:

    Esta implementación utiliza un ciclo de this.waitllamadas condicionadas this.isAlive. Cuando termina un subproceso this.notifyAll, se invoca el método. Se recomienda que las aplicaciones no utilizan wait, notifyo notifyAllen Threadcasos.

Stephen C
fuente
2

Como no ha publicado el código, estamos trabajando en la oscuridad. ¿Cuáles son los detalles de la excepción?

¿Estás llamando a Thread.wait () desde dentro del hilo o fuera de él?

Pregunto esto porque de acuerdo con el javadoc para IllegalMonitorStateException, es:

Lanzado para indicar que un subproceso ha intentado esperar en el monitor de un objeto o para notificar a otros subprocesos que esperan en el monitor de un objeto sin poseer el monitor especificado.

Para aclarar esta respuesta, esta llamada a esperar en un hilo también arroja IllegalMonitorStateException, a pesar de ser llamado desde dentro de un bloque sincronizado:


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }
CPerkins
fuente
@CPerkins: Creo que estás confundiendo el hilo de ejecución y el objeto que es el objetivo wait().
Robert Munteanu
@Robert: tal vez lo soy, pero no lo creo. Si inicia una instancia de Thread y luego le pide que espere, obtendrá una IllegalMonitorStateException, que es lo que estaba tratando de describir.
CPerkins
¿Estás hablando de la worker.wait()línea? Entonces deberías estar sincronizando con el trabajador, no en la cerradura.
Robert Munteanu
1

Para tratar con IllegalMonitorStateException, debe verificar que todas las invocaciones de la espera, notificar y notificar Todos los métodos se lleven a cabo solo cuando el subproceso de llamada sea el propietario del monitor apropiado . La solución más simple es encerrar estas llamadas dentro de bloques sincronizados. El objeto de sincronización que se invocará en la declaración sincronizada es aquel cuyo monitor debe ser adquirido.

Aquí está el ejemplo simple para entender el concepto de monitor

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Rakesh Chaudhari
fuente
0

La llamada Thread.wait () tiene sentido dentro de un código que se sincroniza en el objeto Thread.class. No creo que sea lo que quisiste decir.
Usted pregunta

¿Cómo puedo hacer que un hilo espere hasta que sea notificado?

Solo puede hacer que su hilo actual espere. Cualquier otro hilo solo se puede pedir suavemente que espere, si está de acuerdo.
Si desea esperar alguna condición, necesita un objeto de bloqueo (el objeto Thread.class es una muy mala elección), es un AFAIK único, por lo que sincronizarlo (excepto los métodos estáticos de Thread) es peligroso.
Tom Hawtin ya explica los detalles de sincronización y espera. java.lang.IllegalMonitorStateExceptionsignifica que está intentando esperar en un objeto en el que no está sincronizado; es ilegal hacerlo.

Tadeusz Kopec
fuente
0

No estoy seguro de si esto ayudará a alguien más o no, pero esta fue la parte clave para solucionar mi problema en la respuesta anterior del usuario "Tom Hawtin - tacklin":

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Solo el hecho de que el "bloqueo" se pasa como un argumento en sincronizado () y también se usa en "bloqueo" .notifyAll ();

Una vez que lo logré en esos 2 lugares, lo conseguí funcionando

jp093121
fuente
0

Recibí un IllegalMonitorStateExceptiontiempo tratando de despertar un hilo en / desde un classhilo diferente . En java 8puede utilizar las lockcaracterísticas de la nueva API de concurrencia en lugar de synchronizedfunciones.

Ya estaba almacenando objetos para asynchronoustransacciones websocket en a WeakHashMap. La solución en mi caso fue también almacenar un lockobjeto en unConcurrentHashMap para synchronousrespuestas. Tenga en cuenta el condition.await(no .wait).

Para manejar el subprocesamiento múltiple, utilicé a Executors.newCachedThreadPool()para crear un grupo de subprocesos .

Stuart Cardall
fuente
0

Aquellos que usan Java 7.0 o una versión inferior pueden consultar el código que usé aquí y funciona.

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
mannedear
fuente