IllegalMonitorStateException beim Aufruf von wait ()

161

Ich verwende Multithreading in Java für mein Programm. Ich habe den Thread erfolgreich ausgeführt, aber wenn ich ihn verwende Thread.wait(), wird er ausgelöst java.lang.IllegalMonitorStateException. Wie kann ich einen Thread warten lassen, bis er benachrichtigt wird?

prakash.panjwani
quelle
2
Thread.wait () existiert nicht, es könnte this.wait () sein
Premraj

Antworten:

174

Sie müssen sich in einem synchronizedBlock befinden, um Object.wait()arbeiten zu können.

Außerdem empfehle ich, die Parallelitätspakete anstelle der Threading-Pakete der alten Schule zu betrachten. Sie sind sicherer und viel einfacher zu bearbeiten .

Viel Spaß beim Codieren.

BEARBEITEN

Ich nahm an, Sie meinten Object.wait()als Ihre Ausnahme, was passiert, wenn Sie versuchen, Zugriff zu erhalten, ohne die Objektsperre zu halten.

reccles
quelle
1
guter Fang. Ich nahm an, er meinte Object.wait () und rief von einem Thread an
reccles
2
Ein synchronisierter Block für das Objekt, auf das Sie warten. Möchten Sie diese Antwort bearbeiten, um das etwas klarer zu machen? Vielen Dank.
Grau
55

waitist definiert in Objectund nicht es Thread. Der eingeschaltete Monitor Threadist etwas unvorhersehbar.

Obwohl alle Java-Objekte über Monitore verfügen, ist es im Allgemeinen besser, eine dedizierte Sperre zu haben:

private final Object lock = new Object();

Durch die Verwendung einer benannten Klasse können Sie die Diagnose bei geringen Speicherkosten (ca. 2 KB pro Prozess) etwas einfacher lesen:

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

Um zu waitoder notify/ notifyAllein Objekt zu gelangen, müssen Sie die Sperre mit der synchronizedAnweisung halten. Außerdem benötigen Sie eine whileSchleife, um den Weckzustand zu überprüfen (finden Sie einen guten Text zum Threading, um zu erklären, warum).

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

Bemerken:

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

Es lohnt sich, sowohl die Java-Sprache als auch die java.util.concurrent.locksSperren (und java.util.concurrent.atomic) zu verstehen, wenn Sie sich mit Multithreading beschäftigen. Verwenden Sie jedoch java.util.concurrentDatenstrukturen, wann immer Sie können.

Tom Hawtin - Tackline
quelle
5
Ich habe nie verstanden, wie dies funktioniert, da sich Warten und Benachrichtigen beide in synchronisierten Blöcken für dasselbe Objekt befinden (Sperre). Sollte sich der Benachrichtigungsthread nicht in der Zeile "synchronized (lock)" befinden, da sich der Wait-Thread im Block befindet?
Brent212
6
@ Brent212 Für jede andere Methode als wait, ja, du würdest nie dazu kommen notify. In den API-Dokumenten für Object.wait"Der Thread gibt jedoch den Besitz dieses Monitors frei". Während waites so ist, als ob es sich außerhalb der umschließenden synchronizedBlöcke befindet (für dasselbe Objekt können sich mehrere synchronizedBlöcke auf demselben Objekt befinden).
Tom Hawtin - Tackline
24

Ich weiß, dass dieser Thread fast 2 Jahre alt ist, muss ihn aber noch schließen, da ich auch mit demselben Problem zu dieser Q / A-Sitzung gekommen bin ...

Bitte lesen Sie diese Definition von illegalMonitorException immer wieder ...

IllegalMonitorException wird ausgelöst, um anzuzeigen, dass ein Thread versucht hat, auf dem Monitor eines Objekts zu warten oder andere Threads zu benachrichtigen, die auf dem Monitor eines Objekts warten, ohne den angegebenen Monitor zu besitzen.

Diese Zeile sagt immer wieder, dass IllegalMonitorException auftritt, wenn eine der beiden Situationen eintritt ....

1> Warten Sie auf dem Monitor eines Objekts, ohne den angegebenen Monitor zu besitzen.

2> Benachrichtigen Sie andere Threads, die auf dem Monitor eines Objekts warten, ohne den angegebenen Monitor zu besitzen.

Einige haben vielleicht ihre Antworten bekommen ... wer alles nicht, dann überprüfen Sie bitte 2 Aussagen ...

synchronisiert (Objekt)

object.wait ()

Wenn beide Objekte gleich sind ... kann keine illegalMonitorException auftreten.

Lesen Sie jetzt noch einmal die IllegalMonitorException-Definition und Sie werden sie nicht wieder vergessen ...

Mina
quelle
Eigentlich funktioniert das nicht. Ich habe es versucht. Ich erstelle ein Runnable, sperre es (mithilfe eines synchronisierten Blocks) und führe innerhalb dieses Blocks Runnable auf dem UI-Thread (Android) aus. Danach mache ich myRunnable.wait () und erhalte immer noch die Ausnahme.
Ted
Excelente Erklärung !! Ich habe wait () ausgeführt, ohne das Objekt anzugeben, also hat es die Instanz benötigt und auf einem anderen Objekt synchronisiert. Jetzt benutze ich otherObject.wait () und es funktioniert!
Fersca
6

Aufgrund Ihrer Kommentare klingt es so, als würden Sie so etwas tun:

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

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

Es gibt drei Probleme.

  1. Wie andere gesagt haben, obj.wait()kann nur aufgerufen werden, wenn der aktuelle Thread die primitive Sperre / Mutex für enthält obj. Wenn der aktuelle Thread die Sperre nicht hält, wird die angezeigte Ausnahme angezeigt.

  2. Der thread.wait()Anruf macht nicht das, was Sie zu erwarten scheinen. Insbesondere führt thread.wait() dies nicht dazu, dass der nominierte Thread wartet. Vielmehr wartet der aktuelle Thread , bis ein anderer Thread thread.notify()oder aufruft thread.notifyAll().

    Es gibt eigentlich keine sichere Möglichkeit, eine ThreadInstanz zum Anhalten zu zwingen , wenn sie dies nicht möchte. (Das, was Java am nächsten kommt, ist die veraltete Thread.suspend()Methode, aber diese Methode ist von Natur aus unsicher, wie im Javadoc erläutert.)

    Wenn Sie möchten, dass das neu gestartete ThreadProgramm angehalten wird, erstellen Sie am besten eine CountdownLatchInstanz und lassen den Thread-Aufruf await()des Latches anhalten, um sich selbst anzuhalten. Der Haupt-Thread würde dann countDown()die Verriegelung aufrufen , um den angehaltenen Thread fortzusetzen.

  3. Orthogonal zu den vorherigen Punkten kann die Verwendung eines ThreadObjekts als Sperre / Mutex Probleme verursachen. Zum Beispiel Thread::joinsagt der Javadoc für :

    Diese Implementierung verwendet eine Aufrufschleife, die von einer this.waitBedingung abhängig ist this.isAlive. Wenn ein Thread beendet wird, wird die this.notifyAllMethode aufgerufen. Es wird empfohlen , dass Anwendungen nicht verwenden wait, notifyoder notifyAllauf ThreadInstanzen.

Stephen C.
quelle
2

Da Sie keinen Code gepostet haben, arbeiten wir im Dunkeln. Was sind die Details der Ausnahme?

Rufen Sie Thread.wait () innerhalb oder außerhalb des Threads auf?

Ich frage dies, weil es laut Javadoc für IllegalMonitorStateException ist:

Wird ausgelöst, um anzuzeigen, dass ein Thread versucht hat, auf dem Monitor eines Objekts zu warten, oder um andere Threads zu benachrichtigen, die auf dem Monitor eines Objekts warten, ohne den angegebenen Monitor zu besitzen.

Um diese Antwort zu verdeutlichen, löst dieser Aufruf zum Warten auf einen Thread auch IllegalMonitorStateException aus, obwohl er aus einem synchronisierten Block heraus aufgerufen wird:


     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
quelle
@CPerkins: Ich denke, Sie verwechseln den Ausführungsthread und das Objekt, dessen Ziel es ist wait().
Robert Munteanu
@ Robert - Vielleicht bin ich, aber ich denke nicht. Wenn Sie eine Thread-Instanz starten und sie dann zum Warten auffordern, wird eine IllegalMonitorStateException angezeigt, die ich beschreiben wollte.
CPerkins
Sprechen Sie über die worker.wait()Linie? Dann sollten Sie auf dem Worker synchronisieren, nicht auf dem Schloss.
Robert Munteanu
1

Um mit der IllegalMonitorStateException umgehen zu können, müssen Sie überprüfen, ob alle Aufrufe der Methoden wait, notify und notifyAll nur stattfinden, wenn der aufrufende Thread den entsprechenden Monitor besitzt . Die einfachste Lösung besteht darin, diese Aufrufe in synchronisierte Blöcke einzuschließen. Das Synchronisationsobjekt, das in der synchronisierten Anweisung aufgerufen werden soll, ist dasjenige, dessen Monitor erfasst werden muss.

Hier ist das einfache Beispiel, um das Konzept des Monitors zu verstehen

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
quelle
0

Der Aufruf von Thread.wait () ist in einem Code sinnvoll, der mit dem Thread.class-Objekt synchronisiert wird. Ich glaube nicht, dass du das gemeint hast.
Du fragst

Wie kann ich einen Thread warten lassen, bis er benachrichtigt wird?

Sie können nur Ihren aktuellen Thread warten lassen. Jeder andere Thread kann nur sanft zum Warten aufgefordert werden, wenn er zustimmt.
Wenn Sie auf eine Bedingung warten möchten, benötigen Sie ein Sperrobjekt - Thread.class-Objekt ist eine sehr schlechte Wahl - es ist ein Singleton-AFAIK, daher ist die Synchronisierung darauf (mit Ausnahme der statischen Thread-Methoden) gefährlich.
Details zur Synchronisation und zum Warten werden bereits von Tom Hawtin erklärt. java.lang.IllegalMonitorStateExceptionbedeutet, dass Sie versuchen, auf ein Objekt zu warten, auf dem Sie nicht synchronisiert sind - dies ist illegal.

Tadeusz Kopec
quelle
0

Ich bin mir nicht sicher, ob dies jemand anderem helfen wird oder nicht, aber dies war der Schlüssel zur Behebung meines Problems in der obigen Antwort von Benutzer "Tom Hawtin - Tacklin":

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

Nur die Tatsache, dass die "Sperre" als Argument in synchronized () übergeben wird und auch in "lock" .notifyAll () verwendet wird;

Sobald ich es an diesen 2 Orten geschafft habe, habe ich es zum Laufen gebracht

jp093121
quelle
0

Ich habe eine IllegalMonitorStateExceptionWeile versucht, einen Thread in / von einem anderen class/ Thread aufzuwecken . In können java 8Sie die lockFunktionen der neuen Concurrency-API anstelle von synchronizedFunktionen verwenden.

Ich habe bereits Objekte für asynchronousWebsocket-Transaktionen in einem gespeichert WeakHashMap. Die Lösung in meinem Fall bestand darin , ein lockObjekt auch in einemConcurrentHashMap für synchronousAntworten zu speichern . Beachten Sie die condition.await(nicht .wait).

Um das Multithreading zu handhaben, habe ich a verwendet Executors.newCachedThreadPool(), um einen Thread-Pool zu erstellen .

Stuart Cardall
quelle
0

Diejenigen, die Java 7.0 oder eine niedrigere Version verwenden, können auf den Code verweisen, den ich hier verwendet habe, und er funktioniert.

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
    }

}
bemannt
quelle