Wie verwende ich wait und notify in Java ohne IllegalMonitorStateException?


129

Ich habe 2 Matrizen und ich muss sie multiplizieren und dann die Ergebnisse jeder Zelle drucken. Sobald eine Zelle bereit ist, muss ich sie drucken, aber zum Beispiel muss ich die Zelle [0] [0] vor Zelle [2] [0] drucken, selbst wenn das Ergebnis von [2] [0] zuerst bereit ist . Also muss ich es auf Bestellung ausdrucken. Meine Idee ist es also, den Druckerthread warten zu lassen, bis er multiplyThreadbenachrichtigt, dass die richtige Zelle zum Drucken bereit ist, und dann printerThreaddie Zelle zu drucken und wieder zu warten und so weiter.

Also habe ich diesen Thread, der die Multiplikation macht:

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

Thread, der das Ergebnis jeder Zelle druckt:

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

Jetzt wirft es mir diese Ausnahmen:

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)

Zeile 49 in multiplyThreadist das "notify ()". Ich denke, ich muss das synchronisierte anders verwenden, aber ich bin nicht sicher, wie.

Wenn jemand helfen kann, dass dieser Code funktioniert, werde ich ihn wirklich schätzen.

Antworten:


215

Um notify () aufrufen zu können, müssen Sie für dasselbe Objekt synchronisieren.

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

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

29
Die while(!JobCompleted);Option ist im Allgemeinen eine schlechte Idee, da sie Ihre CPU zu 100% bindet und ständig dieselbe Variable überprüft (siehe hier )
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); hat dieses Problem nicht
BeniBela

15
Es hat immer noch das Problem, etwas völlig anderes zu sein. Das Abrufen (wiederholtes Überprüfen, ob eine Bedingung erfüllt ist, dh was Sie tun) ist im Allgemeinen weniger bevorzugt als benachrichtigt zu werden, wenn diese Bedingung geändert wird (dh was ich in der Antwort dargelegt habe).
Bombe

3
@huseyintugrulbuyukisik können Sie aufrufen, waitwenn der aktuelle Thread eine Sperre für das Objekt hat, für das waitaufgerufen wird. Ob Sie einen synchronizedBlock oder eine synchronisierte Methode verwenden, liegt ganz bei Ihnen.
Bombe

1
@BeniBela Aber es ist sicher zu erwarten, dass es langsamer wird (in Bezug auf Huseyins ursprüngliche Frage).
Thomas

64

Bei der Verwendung der Methoden waitund notifyund notifyAllin Java müssen folgende Dinge beachtet werden:

  1. Verwenden Sie notifyAllanstelle von, notifywenn Sie erwarten, dass mehr als ein Thread auf eine Sperre wartet.
  2. Die Methoden waitund notifymüssen in einem synchronisierten Kontext aufgerufen werden . Weitere Informationen finden Sie unter dem Link.
  3. Rufen Sie die wait()Methode immer in einer Schleife auf, denn wenn mehrere Threads auf eine Sperre warten und einer von ihnen die Sperre erhalten und die Bedingung zurückgesetzt hat, müssen die anderen Threads die Bedingung nach dem Aufwachen überprüfen, um festzustellen, ob sie erneut warten müssen oder kann mit der Verarbeitung beginnen.
  4. Verwenden Sie dasselbe Objekt für den Aufruf wait()und die notify()Methode. Jedes Objekt hat eine eigene Sperre, sodass das Aufrufen wait()von Objekt A und notify()Objekt B keinen Sinn ergibt.

21

Müssen Sie das überhaupt einfädeln? Ich frage mich, wie groß Ihre Matrizen sind und ob es von Vorteil ist, wenn ein Thread gedruckt wird, während der andere die Multiplikation durchführt.

Vielleicht lohnt es sich, diese Zeit vor der relativ komplexen Einfädelarbeit zu messen?

Wenn Sie es einfädeln müssen, würde ich 'n' Threads erstellen, um die Multiplikation der Zellen durchzuführen (vielleicht ist 'n' die Anzahl der Kerne, die Ihnen zur Verfügung stehen), und dann den ExecutorService- und Future- Mechanismus verwenden, um mehrere Multiplikationen gleichzeitig zu versenden .

Auf diese Weise können Sie die Arbeit basierend auf der Anzahl der Kerne optimieren und verwenden die übergeordneten Java-Threading-Tools (die das Leben erleichtern sollten). Schreiben Sie die Ergebnisse zurück in eine Empfangsmatrix und drucken Sie diese einfach aus, sobald alle Ihre zukünftigen Aufgaben abgeschlossen sind.


1
+1 @Greg Ich denke, Sie sollten sich das Paket java.util.concurrent ansehen, wie Brian betont hat.
ATorras

1
+1 und lesen
Chii

14

Angenommen, Sie haben eine Black-Box-Anwendung mit einer Klasse namens BlackBoxClassmethod doSomething();.

Außerdem haben Sie einen Beobachter oder Zuhörer benannt onResponse(String resp), der BlackBoxClassnach unbekannter Zeit angerufen wird .

Der Ablauf ist einfach:

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

Nehmen wir an, wir wissen nicht, was los ist BlackBoxClassund wann wir eine Antwort erhalten sollen, aber Sie möchten Ihren Code erst fortsetzen, wenn Sie eine Antwort erhalten oder mit anderen Worten einen onResponseAnruf erhalten. Hier wird 'Helfer synchronisieren' eingegeben:

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) {
        }
    }
}
}

Jetzt können wir implementieren, was wir wollen:

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

Sie können Benachrichtigungen nur für Objekte aufrufen, deren Monitor Ihnen gehört. Also brauchst du so etwas wie

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


3

Ich werde gleich ein einfaches Beispiel zeigen, wie man es richtig benutzt waitund notifyin Java. Also werde ich zwei Klassen mit dem Namen ThreadA & ThreadB erstellen . ThreadA ruft ThreadB auf.

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

und für Klasse 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

Einfache Verwendung, wenn Sie möchten So führen Sie Threads alternativ aus: -

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

Antwort :-

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

Wie funktioniert das, wenn ich 4 Operationen synchron ausführen muss?
Saksham Agarwal

2

Wir können notify aufrufen, um die Ausführung wartender Objekte als fortzusetzen

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

Setzen Sie dies fort, indem Sie notify für ein anderes Objekt derselben Klasse aufrufen

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

0

Speichern Sie für dieses spezielle Problem Ihre verschiedenen Ergebnisse in Variablen. Wenn der letzte Teil Ihres Threads verarbeitet wird, können Sie in einem beliebigen Format drucken. Dies ist besonders nützlich, wenn Sie Ihre Arbeitshistorie in anderen Projekten verwenden möchten.


0

Dies scheint eine Situation für das Produzenten-Konsumenten-Muster zu sein. Wenn Sie Java 5 oder höher verwenden, können Sie die Blockierungswarteschlange (java.util.concurrent.BlockingQueue) verwenden und die Thread-Koordinierungsarbeit der zugrunde liegenden Framework- / API-Implementierung überlassen. Siehe das Beispiel aus Java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html oder Java 7 (dasselbe Beispiel): http: // docs. oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html


0

Sie haben Ihren Codeblock ordnungsgemäß geschützt, wenn Sie die wait()Methode mit aufrufen synchronized(this).

Sie haben jedoch nicht die gleiche Vorsichtsmaßnahme getroffen, wenn Sie die notify()Methode aufrufen , ohne einen geschützten Block zu verwenden: synchronized(this)odersynchronized(someObject)

Wenn Sie auf der Oracle - Dokumentation Seite auf beziehen Objektklasse, die enthält wait(), notify(), notifyAll()Methoden, können Sie unten vorsorglich in all diesen drei Methoden sehen

Diese Methode sollte nur von einem Thread aufgerufen werden, der Eigentümer des Monitors dieses Objekts ist

Viele Dinge wurden in den letzten 7 Jahren geändert und wir wollen uns andere Alternativen zu den synchronizedfolgenden SE-Fragen ansehen :

Warum ein ReentrantLock verwenden, wenn man synchronisiert (dies) verwenden kann?

Synchronisation gegen Sperre

Vermeiden Sie (dies) in Java synchronisiert?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.