IllegalMonitorStateException su wait () call


162

Sto usando il multi-threading in Java per il mio programma. Ho eseguito correttamente il thread, ma quando lo sto usando Thread.wait(), sta lanciando java.lang.IllegalMonitorStateException. Come posso attendere che un thread venga avvisato?


2
Thread.wait () non esiste, potrebbe essere this.wait ()
Premraj

Risposte:


175

Devi essere in un synchronizedblocco per Object.wait()funzionare.

Inoltre, raccomando di esaminare i pacchetti di concorrenza anziché i pacchetti di threading della vecchia scuola. Sono più sicuri e molto più facili da lavorare .

Buona codifica.

MODIFICARE

Supponevo che intendessi Object.wait()come eccezione, ciò che accade quando provi ad accedere senza tenere il blocco degli oggetti.


1
buona pesca. ho pensato che intendesse Object.wait () e chiamato da un thread
reccles

2
Un blocco sincronizzato sull'oggetto in attesa. Vuoi modificare questa risposta per renderlo un po 'più chiaro? Grazie.
Gray,

55

waitè definito in Object, e non in esso Thread. Il monitor acceso Threadè un po 'imprevedibile.

Sebbene tutti gli oggetti Java abbiano monitor, in genere è meglio avere un blocco dedicato:

private final Object lock = new Object();

Puoi leggere un po 'più facilmente la diagnostica, con un piccolo costo di memoria (circa 2K per processo) usando una classe denominata:

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

Per waito notify/ notifyAllun oggetto, devi tenere il lucchetto con l' synchronizedistruzione. Inoltre, avrai bisogno di un whileciclo per verificare la condizione di sveglia (trova un buon testo sul threading per spiegare il perché).

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

Notificare:

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

Vale la pena comprendere sia il linguaggio Java che i java.util.concurrent.locksblocchi (e java.util.concurrent.atomic) quando si accede al multithreading. Ma usa java.util.concurrentle strutture dati ogni volta che puoi.


5
Non ho mai capito come funziona, dato che l'attesa e la notifica sono entrambe in blocchi sincronizzati sullo stesso oggetto (blocco). Dal momento che il thread di attesa è nel blocco, questo non dovrebbe rendere il blocco del thread di notifica sulla linea "sincronizzata (blocco)"?
Brent212,

6
@ Brent212 Per qualsiasi metodo diverso da wait, sì, non ci riusciresti mai notify. Tuttavia, nei documenti API per Object.wait"Il thread rilascia la proprietà di questo monitor". Quindi, mentre waitè come se fosse al di fuori dei synchronizedblocchi racchiusi (per lo stesso oggetto, possono esserci più synchronizedblocchi sullo stesso oggetto).
Tom Hawtin - affronta il

24

So che questo thread ha quasi 2 anni ma devo ancora chiuderlo poiché sono arrivato anche a questa sessione di domande e risposte con lo stesso problema ...

Leggere più volte questa definizione di illegalMonitorException ...

IllegalMonitorException viene generato per indicare che un thread ha tentato di attendere sul monitor di un oggetto o di avvisare altri thread in attesa sul monitor di un oggetto senza possedere il monitor specificato.

Questa riga dice ancora e ancora, IllegalMonitorException arriva quando si verifica una delle 2 situazioni ....

1> attendere sul monitor di un oggetto senza possedere il monitor specificato.

2> notifica ad altri thread in attesa sul monitor di un oggetto senza possedere il monitor specificato.

Alcuni potrebbero avere le loro risposte ... chi non lo fa, quindi controlla 2 dichiarazioni ...

sincronizzato (oggetto)

object.wait ()

Se entrambi gli oggetti sono uguali ... non può arrivare nessuna illegalitàMonitorException.

Ora leggi di nuovo la definizione di IllegalMonitorException e non la dimenticherai più ...


In realtà, non funziona. L'ho provato Creo un Runnable, lo blocco (usando il blocco sincronizzato) e all'interno di quel blocco eseguo Runnable sul thread dell'interfaccia utente (Android) e successivamente eseguo myRunnable.wait () e ottengo ancora l'eccezione.
Ted

Spiegazione Excelente !! Stavo facendo wait () senza specificare l'oggetto, quindi ho preso l'istanza e la sincronizzazione su un altro oggetto. Ora sto usando otherObject.wait () e funziona!
Fersca,

6

Sulla base dei tuoi commenti sembra che tu stia facendo qualcosa del genere:

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

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

Ci sono tre problemi.

  1. Come altri hanno già detto, obj.wait()può essere chiamato solo se il thread corrente contiene il blocco / mutex primitivo per obj. Se il thread corrente non contiene il blocco, ottieni l'eccezione che stai vedendo.

  2. La thread.wait()chiamata non fa ciò che sembra aspettarsi che faccia. In particolare, thread.wait() non fa attendere il thread nominato. Piuttosto, fa sì che il thread corrente attenda fino a quando qualche altro thread chiama thread.notify()o thread.notifyAll().

    In realtà non esiste un modo sicuro per forzare Threadun'istanza a mettere in pausa se non lo desidera. (Il più vicino che Java ha a questo è il Thread.suspend()metodo deprecato , ma quel metodo è intrinsecamente pericoloso, come spiegato in Javadoc.)

    Se si desidera Threadmettere in pausa il nuovo avvio , il modo migliore per farlo è creare CountdownLatchun'istanza e fare in modo che la chiamata di thread await()sul latch si metta in pausa. Il thread principale chiamerebbe quindi countDown()il latch per consentire al thread sospeso di continuare.

  3. Ortogonale ai punti precedenti, l'utilizzo di un Threadoggetto come blocco / mutex può causare problemi. Ad esempio, il javadoc per Thread::joindice:

    Questa implementazione utilizza un ciclo di this.waitchiamate condizionate this.isAlive. Quando un thread termina this.notifyAll, viene invocato il metodo. Si raccomanda di non utilizzare le applicazioni wait, notifyo notifyAllsu Threadistanze.


2

Dal momento che non hai pubblicato il codice, stiamo lavorando al buio. Quali sono i dettagli dell'eccezione?

Stai chiamando Thread.wait () dall'interno del thread o all'esterno di esso?

Lo chiedo perché secondo javadoc per IllegalMonitorStateException, è:

Generato per indicare che un thread ha tentato di attendere sul monitor di un oggetto o di avvisare altri thread in attesa sul monitor di un oggetto senza possedere il monitor specificato.

Per chiarire questa risposta, questa chiamata ad attendere su un thread genera anche IllegalMonitorStateException, nonostante venga chiamata da un blocco sincronizzato:


     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: penso che tu stia confondendo il thread di esecuzione e l'oggetto di cui è oggetto wait().
Robert Munteanu,

@Robert - Forse lo sono, ma non la penso così. Se avvii un'istanza di Thread e poi chiedi di aspettare, otterrai un IllegalMonitorStateException, che è quello che stavo tentando di descrivere.
CPerkins,

Stai parlando della worker.wait()linea? Quindi dovresti sincronizzare il lavoratore, non il blocco.
Robert Munteanu,

1

Per gestire IllegalMonitorStateException, è necessario verificare che tutte le chiamate di attesa, notifica e notifica Tutti i metodi avvengano solo quando il thread chiamante possiede il monitor appropriato . La soluzione più semplice è racchiudere queste chiamate all'interno di blocchi sincronizzati. L'oggetto di sincronizzazione che deve essere invocato nell'istruzione sincronizzata è quello il cui monitor deve essere acquisito.

Ecco il semplice esempio per comprendere il concetto di monitor

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

0

La chiamata Thread.wait () ha senso all'interno di un codice che si sincronizza sull'oggetto Thread.class. Non penso sia quello che volevi dire.
Tu chiedi

Come posso attendere che un thread venga avvisato?

Puoi far aspettare solo il tuo thread attuale. Qualsiasi altro thread può essere gentilmente invitato ad attendere, se concordato.
Se vuoi aspettare qualche condizione, hai bisogno di un oggetto lock - L'oggetto Thread.class è una scelta pessima - è un AFAIK singleton, quindi la sincronizzazione su di esso (ad eccezione dei metodi statici Thread) è pericolosa.
I dettagli per la sincronizzazione e l'attesa sono già stati spiegati da Tom Hawtin. java.lang.IllegalMonitorStateExceptionsignifica che stai provando ad aspettare un oggetto su cui non sei sincronizzato - è illegale farlo.


0

Non sono sicuro che questo possa aiutare qualcun altro o no, ma questa è stata la parte fondamentale per risolvere il mio problema nella risposta sopra "Tom Hawtin - tacklin" dell'utente:

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

Solo il fatto che il "lock" sia passato come argomento in synchronized () e sia anche usato in "lock" .notifyAll ();

Una volta che l'ho fatto in quei 2 posti l'ho fatto funzionare


0

Ho ricevuto un IllegalMonitorStateExceptionpo 'di tempo cercando di svegliare un thread in / da un altro class/ thread. In java 8è possibile utilizzare le lockcaratteristiche del nuovo Concurrency API invece di synchronizedfunzioni.

Stavo già memorizzando oggetti per asynchronoustransazioni di websocket in a WeakHashMap. La soluzione nel mio caso era anche quella di memorizzare un lockoggetto in aConcurrentHashMap per le synchronousrisposte. Nota il condition.await(non .wait).

Per gestire il multi threading ho usato a Executors.newCachedThreadPool()per creare un pool di thread .


0

Coloro che utilizzano Java 7.0 o versione precedente possono fare riferimento al codice che ho usato qui e funziona.

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
    }

}
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.