Come usare aspettare e notificare in Java senza IllegalMonitorStateException?


129

Ho 2 matrici e devo moltiplicarle e quindi stampare i risultati di ogni cella. Non appena una cella è pronta, devo stamparla, ma ad esempio devo stampare la cella [0] [0] prima della cella [2] [0] anche se il risultato di [2] [0] è pronto per primo . Quindi devo stamparlo per ordine. Quindi la mia idea è di far attendere il thread della stampante fino a quando multiplyThreadnon viene notificato che la cella corretta è pronta per essere stampata e quindi la printerThreadstampa la cella e torna in attesa e così via ..

Quindi ho questo thread che fa la moltiplicazione:

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

Discussione che stampa il risultato di ogni cella:

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] + " ");
            }
        }
    }

Ora mi lancia queste eccezioni:

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 riga 49 in multiplyThreadè "notify ()" .. Penso di dover usare il sincronizzato in modo diverso ma non sono sicuro di come.

Se qualcuno può aiutare questo codice a funzionare, lo apprezzerò davvero.

Risposte:


215

Per poter chiamare Notify () è necessario sincronizzare sullo stesso oggetto.

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

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}

29
L' while(!JobCompleted);opzione è generalmente una cattiva idea perché lega la tua CPU al 100% controllando costantemente la stessa variabile (vedi qui )
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); non ha questo problema
BeniBela il

15
Ha ancora il problema di essere qualcosa di completamente diverso. Il polling (verificare ripetutamente se si verifica una condizione, ad esempio cosa si sta facendo) è generalmente meno preferito che ricevere una notifica se tale condizione viene modificata (ovvero ciò che ho indicato nella risposta).
Bombe,

3
@huseyintugrulbuyukisik puoi chiamare waitogni volta che il thread corrente ha un blocco sull'oggetto su cui waitviene chiamato. Se usi un synchronizedblocco o un metodo sincronizzato dipende completamente da te.
Bombe,

1
@BeniBela Ma ci si può aspettare che sia più lento (per quanto riguarda la domanda originale di Huseyin).
Thomas,

64

Durante l'utilizzo dei metodi waite notifyo notifyAllin Java, è necessario ricordare le seguenti cose:

  1. Utilizzare notifyAllinvece di notifyse si prevede che più di un thread sarà in attesa di un blocco.
  2. I metodi waite notifydevono essere chiamati in un contesto sincronizzato . Vedi il link per una spiegazione più dettagliata.
  3. Chiama sempre il wait()metodo in un ciclo perché se più thread sono in attesa di un blocco e uno di essi ha ottenuto il blocco e ripristinato la condizione, gli altri thread devono verificare la condizione dopo essersi svegliati per vedere se devono attendere di nuovo o può iniziare l'elaborazione.
  4. Utilizzare lo stesso oggetto per la chiamata wait()e il notify()metodo; ogni oggetto ha il suo lucchetto, quindi chiamare wait()sull'oggetto A e notify()sull'oggetto B non avrà alcun senso.

21

Hai bisogno di thread tutto questo? Mi chiedo quanto siano grandi le tue matrici e se ci sia qualche vantaggio nell'avere un filo stampato mentre l'altro fa la moltiplicazione.

Forse varrebbe la pena misurare questa volta prima di fare il lavoro di threading relativamente complesso?

Se hai bisogno di thread, creerei 'n' thread per eseguire la moltiplicazione delle celle (forse 'n' è il numero di core disponibili per te), e quindi utilizzare ExecutorService and Future meccanismo per inviare più moltiplicazioni contemporaneamente .

In questo modo puoi ottimizzare il lavoro in base al numero di core e stai utilizzando gli strumenti di threading Java di livello superiore (che dovrebbero semplificare la vita). Riscrivi i risultati in una matrice ricevente, quindi stampalo semplicemente una volta completate tutte le attività future.


1
+1 @Greg Penso che dovresti dare un'occhiata al pacchetto java.util.concurrent, come sottolineato da Brian.
ATorras,

1
+1 e dai un'occhiata anche a questo libro che ti insegnerà anche il modo corretto di usare wait () e notification () jcip.net
Chii,

14

Supponiamo che tu abbia un'applicazione "scatola nera" con una classe chiamata BlackBoxClassche ha metodo doSomething();.

Inoltre, hai un osservatore o un ascoltatore chiamato onResponse(String resp)che verrà chiamato BlackBoxClassdopo un tempo sconosciuto.

Il flusso è semplice:

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

Diciamo che non sappiamo cosa sta succedendo BlackBoxClasse quando dovremmo ricevere una risposta, ma non vuoi continuare il tuo codice fino a quando non ricevi una risposta o in altre parole ricevi una onResponsechiamata. Qui entra 'Sincronizza 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) {
        }
    }
}
}

Ora possiamo implementare ciò che vogliamo:

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

}

7

Puoi chiamare la notifica solo sugli oggetti di cui sei proprietario. Quindi hai bisogno di qualcosa del genere

synchronized(threadObject)
{
   threadObject.notify();
}


3

Vado a destra semplice esempio ti mostrerò il modo giusto di usare waite notifyin Java. Quindi creerò due classi di nome ThreadA e ThreadB . ThreadA chiamerà 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);
        }
    }
} 

e per la classe Thread B:

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

Utilizzo semplice se si desidera eseguire i thread in alternativa: -

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

risposta: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

Come funziona quando ho 4 operazioni da eseguire in modo sincrono?
Saksham Agarwal,

2

possiamo chiamare avvisare per riprendere l'esecuzione di oggetti in attesa come

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

riprendilo invocando notifica su un altro oggetto della stessa classe

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

0

Per questo particolare problema, perché non archiviare i vari risultati in variabili e poi, quando viene elaborato l'ultimo thread, è possibile stampare nel formato desiderato. Ciò è particolarmente utile se utilizzerai la cronologia del tuo lavoro in altri progetti.


0

Sembra una situazione per il modello produttore-consumatore. Se si utilizza java 5 o versioni successive, è possibile prendere in considerazione l'utilizzo della coda di blocco (java.util.concurrent.BlockingQueue) e lasciare che il coordinamento del thread funzioni nell'implementazione framework / api sottostante. Vedi l'esempio di java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html o java 7 (stesso esempio): http: // docs. oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html


0

Hai protetto correttamente il tuo blocco di codice quando chiami wait()metodo usando synchronized(this).

Ma non hai preso la stessa precauzione quando chiami il notify()metodo senza usare il blocco protetto: synchronized(this)osynchronized(someObject)

Se si fa riferimento alla pagina di documentazione Oracle su oggetti di classe, che contiene wait(), notify(), notifyAll()i metodi, si può vedere qui sotto precauzione in tutti questi tre metodi

Questo metodo deve essere chiamato solo da un thread proprietario del monitor di questo oggetto

Molte cose sono state cambiate negli ultimi 7 anni e diamo un'occhiata ad altre alternative alle synchronizeddomande su SE seguenti:

Perché usare un ReentrantLock se si può usare sincronizzato (questo)?

Sincronizzazione vs blocco

Evitare sincronizzato (questo) in Java?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.