¿Cómo utilizar esperar y notificar en Java sin IllegalMonitorStateException?

129

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 . Entonces necesito imprimirlo por orden. Entonces, mi idea es hacer que el hilo de la impresora espere hasta que multiplyThreadle notifique que la celda correcta está lista para imprimirse y luego printerThreadimprimirá la celda y volverá a esperar, etc.

Entonces tengo 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 arroja 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 multiplyThreades "notificar ()". Creo que necesito usar el sincronizado de manera diferente, pero no estoy seguro de cómo hacerlo.

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

Radiodef
fuente

Respuestas:

215

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

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

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}
Bombe
fuente
29
La while(!JobCompleted);opción es generalmente una mala idea porque vincula su CPU al 100% comprobando la misma variable constantemente (ver aquí )
Matt Lyons
55
while(!JobCompleted) Thread.sleep(5); no tiene ese problema
BeniBela
15
Todavía tiene el problema de ser algo completamente diferente. El sondeo (verificar repetidamente si se cumple alguna condición, es decir, lo que está haciendo) generalmente es menos preferido que recibir una notificación si dicha condición cambia (es decir, lo que describí en la respuesta).
Bombe
3
@huseyintugrulbuyukisik puede llamar waitsiempre que el hilo actual tenga un bloqueo en el objeto en el que waitse llama. Si usa un synchronizedbloque o un método sincronizado es completamente su decisión .
Bombe
1
@BeniBela Pero se puede esperar que sea más lento (con respecto a la pregunta original de Huseyin).
Thomas
64

Al usar los métodos waity notifyo notifyAllen Java, se deben recordar las siguientes cosas:

  1. Use en notifyAlllugar de notifysi espera que más de un hilo esté esperando un bloqueo.
  2. Los métodos waity notifydeben llamarse en un contexto sincronizado . Vea el enlace para una explicación más detallada.
  3. Siempre llame al wait()método en un bucle porque si varios subprocesos esperan un bloqueo y uno de ellos obtuvo el bloqueo y restableció la condición, los otros subprocesos deben verificar la condición después de que se despierten para ver si necesitan esperar nuevamente o puede comenzar a procesar.
  4. Use el mismo objeto para llamar wait()y notify()método; cada objeto tiene su propia cerradura, por lo que llamar wait()al objeto A y notify()al objeto B no tendrá ningún sentido.
Jackob
fuente
21

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

¿Quizás valdría la pena medir este tiempo antes de hacer el trabajo de enhebrado relativamente complejo?

Si necesita enhebrarlo, crearía 'n' subprocesos para realizar la multiplicación de las celdas (tal vez 'n' es el número de núcleos disponibles para usted), y luego usaría el mecanismo ExecutorService y Future para despachar múltiples multiplicaciones simultáneamente .

De esa manera, puede optimizar el trabajo en función de la cantidad de núcleos, y está utilizando las herramientas de subprocesamiento Java de nivel superior (que deberían facilitarle la vida). Vuelva a escribir los resultados en una matriz receptora y luego simplemente imprímalo una vez que se hayan completado todas sus tareas futuras.

Brian Agnew
fuente
1
+1 @Greg Creo que deberías echar un vistazo al paquete java.util.concurrent, como lo señaló Brian.
ATorras
1
+1 y también echa un vistazo a este libro que también te enseñará la forma correcta de usar wait () y notify
Chii
14

Digamos que tiene una aplicación de 'caja negra' con alguna clase llamada BlackBoxClassque tiene método doSomething();.

Además, tiene un observador u oyente llamado onResponse(String resp)que será llamado BlackBoxClassdespué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á sucediendo BlackBoxClassy cuándo deberíamos obtener una respuesta, pero no desea continuar su código hasta que obtenga la respuesta o, en otras palabras, onResponsellame. Aquí ingresa '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 queramos:

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

}
Maxim Shoustin
fuente
7

Solo puede llamar a notificar sobre los objetos donde posee su monitor. Entonces necesitas algo como

synchronized(threadObject)
{
   threadObject.notify();
}
PaulJWilliams
fuente
6

notify() necesita ser sincronizado también

takete.dk
fuente
3

Voy a ver un ejemplo simple que te muestra la forma correcta de usar waity notifyen Java. Así que crearé dos clases llamadas ThreadA y 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
            }
        }
    }
BERGUIGA Mohamed Amine
fuente
3

Uso simple si quieres Cómo ejecutar hilos 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();
    }
}

respuesta: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
Opster Elasticsearch Pro-Vijay
fuente
¿Cómo funciona esto cuando tengo 4 operaciones para realizar de forma síncrona?
saksham agarwal
2

podemos llamar a notificar 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!");
}

reanudar esto invocando notificar sobre otro objeto de la misma clase

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}
Greesh Kumar
fuente
0

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

kbluue
fuente
0

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.util.concurrent.BlockingQueue) y dejar el trabajo de coordinación del subproceso a la implementación subyacente de framework / api. Vea 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

usuario3044236
fuente
0

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

Pero no ha tomado la misma precaución cuando llama al notify()método sin usar el bloque protegido: synchronized(this)osynchronized(someObject)

Si se hace referencia a la página de documentación de Oracle en el objeto de clase, 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 subproceso que sea el propietario del monitor de este objeto

Se han cambiado muchas cosas en los últimos 7 años y veamos otras alternativas a las synchronizedsiguientes preguntas SE:

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

Sincronización vs bloqueo

¿Evitar sincronizado (esto) en Java?

Ravindra babu
fuente