¿Cómo usar wait and notify en Java sin IllegalMonitorStateException?


Tengo 2 matrices y necesito multiplicarlas y luego imprimir los resultados de cada celda. Tan pronto como una celda esté lista necesito imprimirla, pero por ejemplo necesito imprimir la celda [0][0] antes de la celda [2][0] incluso si el resultado de [2][0] está listo primero. Así que necesito imprimirlo por orden. Así que mi idea es hacer que el hilo de la impresora espere hasta que el multiplyThread le notifique que la celda correcta está lista para imprimirse y luego el printerThread imprimirá la celda y volverá a esperar y así sucesivamente..

Así que tenga este hilo que hace la multiplicación:

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

Hilo que imprime el resultado de cada celda:

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

Ahora me lanza estas excepciones:

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

La línea 49 en multiplyThread es "notify()"..Creo que necesito usar el sincronizado de manera diferente, pero no estoy seguro de cómo.

Si alguien puede ayudar a que este código funcione, realmente lo apreciaré.

Author: Radiodef, 2009-05-20

12 answers

Para poder llamar a notify() necesita sincronizar en el mismo objeto.

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}
 204
Author: Bombe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-20 08:40:30

Mientras se usan los métodos wait y notify o notifyAll en Java, se deben recordar las siguientes cosas:

  1. Use notifyAll en lugar de notify si espera que más de un hilo esté esperando un bloqueo.
  2. Los métodos wait y notify deben ser llamados en un contexto sincronizado. Vea el enlace para una explicación más detallada.
  3. Siempre llame al método wait() en un bucle porque si varios hilos están esperando un bloqueo y uno de ellos obtuvo el bloqueo y restablezca la condición, luego los otros hilos deben verificar la condición después de que se despierten para ver si necesitan esperar nuevamente o pueden comenzar el procesamiento.
  4. Use el mismo objeto para llamar a los métodos wait() y notify(); cada objeto tiene su propio bloqueo, por lo que llamar a wait() en el objeto A y notify() en el objeto B no tendrá ningún sentido.
 63
Author: Jackob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-21 15:05:31

¿Necesitas enhebrar esto? Me pregunto qué tan grandes son sus matrices, y si hay algún beneficio en tener una impresión de hilo mientras que el otro hace la multiplicación.

Tal vez valdría la pena medir este tiempo antes de hacer el trabajo de roscado relativamente complejo ?

Si necesita enhebrarlo, crearía' n ' threads para realizar la multiplicación de las celdas (quizás 'n' es el número de núcleos disponibles para usted), y luego usaría el ExecutorServicey Future mecanismo para enviar múltiples multiplicaciones simultáneamente.

De esta manera puede optimizar el trabajo en función del número de núcleos, y está utilizando las herramientas de subproceso Java de nivel superior (que deberían facilitar la vida). Escriba los resultados de nuevo en una matriz de recepción, y luego simplemente imprima esto una vez que todas sus tareas futuras se hayan completado.

 20
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-31 14:48:26

Digamos que tienes una aplicación 'black box' con alguna clase llamada BlackBoxClass que tiene método doSomething();.

Además, tiene un observador u oyente llamado onResponse(String resp) que será llamado por BlackBoxClass después de un tiempo desconocido.

El flujo es simple:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

Digamos que no sabemos qué está pasando con BlackBoxClass y cuándo deberíamos obtener respuesta, pero no desea continuar con su código hasta que obtenga respuesta o, en otras palabras, obtenga onResponse llamada. Aquí entra 'Synchronize helper':

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

Ahora podemos implementar lo que queremos:

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}
 13
Author: Maxim Shoustin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-15 22:25:20

Solo puede llamar a notify en objetos donde posee su monitor. Así que necesitas algo como

synchronized(threadObject)
{
   threadObject.notify();
}
 7
Author: PaulJWilliams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-20 13:08:20

notify() necesita ser sincronizado también

 6
Author: takete.dk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-05 03:47:33

Voy a la derecha ejemplo simple le mostrará la forma correcta de usar wait y notify en Java. Así que crearé dos clases llamadas ThreadA & ThreadB . ThreadA llamará a ThreadB.

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

Y para la Clase ThreadB:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }
 3
Author: BERGUIGA Mohamed Amine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-19 09:47:11

Podemos llamar a notify para reanudar la ejecución de objetos en espera como

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

Reanude esto invocando notify en otro objeto de la misma clase

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}
 2
Author: Girish Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-18 10:46:36

Uso simple si desea cómo ejecutar subprocesos alternativamente: -

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}

Responde: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
 1
Author: Vijay Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-10 05:36:50

Para este problema en particular, ¿por qué no almacenar sus diversos resultados en variables y luego cuando el último de su hilo se procesa puede imprimir en el formato que desee. Esto es especialmente útil si vas a usar tu historial de trabajo en otros proyectos.

 0
Author: kbluue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-01 01:52:17

Esto parece una situación para el patrón productor-consumidor. Si está utilizando java 5 o superior, puede considerar el uso de la cola de bloqueo (java.útil.concurrente.BlockingQueue) y dejar el trabajo de coordinación de subprocesos para la implementación del framework/api subyacente. Ver el ejemplo de java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html o java 7 (mismo ejemplo): http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

 0
Author: user3044236,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-06 17:33:20

Ha guardado correctamente su bloque de código cuando llama al método wait() usando synchronized(this).

Pero no has tomado la misma precaución cuando llamas al método notify() sin usar el bloque guardado: synchronized(this) o synchronized(someObject)

Si se refiere a la página de documentación de oracle en la clase Object , que contiene wait() ,notify(), notifyAll() métodos, se puede ver a continuación precaución en todos estos tres métodos

Este método solo debe ser llamado por un hilo que es el propietario de este monitor del objeto

Muchas cosas han cambiado en los últimos 7 años y echemos un vistazo a otras alternativas a synchronized en las siguientes preguntas:

¿Por qué usar un ReentrantLock si se puede usar sincronizado(esto)?

Sincronización vs Bloqueo

Evitar sincronizado (esto) en Java?

 0
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:58