IllegalMonitorStateException lors d'un appel à wait ()


162

J'utilise le multi-threading en java pour mon programme. J'ai exécuté le fil avec succès, mais lorsque j'utilise Thread.wait(), il lance java.lang.IllegalMonitorStateException. Comment puis-je faire attendre un thread jusqu'à ce qu'il soit notifié?


2
Thread.wait () n'existe pas, il pourrait s'agir de this.wait ()
Premraj

Réponses:


175

Vous devez être dans un synchronizedbloc pourObject.wait() pouvoir travailler.

En outre, je recommande de regarder les packages de concurrence au lieu des packages de threads de la vieille école. Ils sont plus sûrs et beaucoup plus faciles à utiliser .

Bon codage.

ÉDITER

J'ai supposé que vous vouliez dire Object.wait()que votre exception est ce qui se passe lorsque vous essayez d'accéder sans maintenir le verrou des objets.


1
bonne prise. J'ai supposé qu'il voulait dire Object.wait () et appelé à partir d'un thread
recycle

2
Un bloc synchronisé sur l'objet que vous attendez. Voulez-vous modifier cette réponse pour que cela soit un peu plus clair? Merci.
Gris

55

waitest défini dans Object, et non dans lui Thread. Le moniteur allumé Threadest un peu imprévisible.

Bien que tous les objets Java aient des moniteurs, il est généralement préférable d'avoir un verrou dédié:

private final Object lock = new Object();

Vous pouvez obtenir un peu plus de facilité de lecture des diagnostics, pour un faible coût de mémoire (environ 2 Ko par processus) en utilisant une classe nommée:

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

Pour waitou notify/ notifyAllun objet, vous devez maintenir le verrou avec l' synchronizedinstruction. De plus, vous aurez besoin d'une whileboucle pour vérifier la condition de réveil (trouvez un bon texte sur le filetage pour expliquer pourquoi).

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

Prévenir:

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

Il vaut la peine de comprendre à la fois le langage Java et les java.util.concurrent.locksverrous (et java.util.concurrent.atomic) lorsque vous vous lancez dans le multithreading. Mais utilisez java.util.concurrentdes structures de données chaque fois que vous le pouvez.


5
Je n'ai jamais compris comment cela fonctionne, étant donné que l'attente et la notification sont toutes deux dans des blocs synchronisés sur le même objet (verrou). Puisque le thread d'attente est dans le bloc, cela ne devrait-il pas faire que le thread de notification se bloque sur la ligne "synchronized (lock)"?
Brent212

6
@ Brent212 Pour toute autre méthode que wait, oui, vous n'y arriverez jamais notify. Cependant, dans la documentation de l'API pour Object.wait, "Le thread libère la propriété de ce moniteur". Donc, pendant waitqu'il y est, c'est comme s'il était en dehors des synchronizedblocs englobants (pour le même objet, il peut y avoir plusieurs synchronizedblocs sur le même objet).
Tom Hawtin - tackline

24

Je sais que ce fil a presque 2 ans mais je dois encore le fermer puisque je suis également venu à cette session de questions / réponses avec le même problème ...

Veuillez lire cette définition de illegalMonitorException encore et encore ...

IllegalMonitorException est levée pour indiquer qu'un thread a tenté d'attendre sur le moniteur d'un objet ou pour notifier d'autres threads en attente sur le moniteur d'un objet sans posséder le moniteur spécifié.

Cette ligne dit encore et encore, IllegalMonitorException survient lorsque l'une des 2 situations se produit ...

1> attendre sur le moniteur d'un objet sans posséder le moniteur spécifié.

2> notifiez les autres threads en attente sur le moniteur d'un objet sans posséder le moniteur spécifié.

Certains ont peut-être obtenu leurs réponses ... qui ne l'ont pas tous, alors veuillez vérifier 2 déclarations ...

synchronisé (objet)

object.wait ()

Si les deux objets sont identiques ... alors aucune exceptionlegalMonitorException ne peut venir.

Maintenant, lisez à nouveau la définition de IllegalMonitorException et vous ne l'oublierez plus ...


En fait, cela ne fonctionne pas. J'ai essayé. Je crée un Runnable, verrouille dessus (en utilisant un bloc synchronisé) et à l'intérieur de ce bloc, je lance Runnable sur le thread UI (Android) et après cela, je fais myRunnable.wait (), et j'obtiens toujours l'exception.
Ted

Excellente explication !! Je faisais wait () sans spécifier l'objet, donc il a pris l'instance et la synchronisation sur un autre objet. Maintenant, j'utilise otherObject.wait () et cela fonctionne!
Fersca

6

D'après vos commentaires, il semble que vous faites quelque chose comme ceci:

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

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

Il y a trois problèmes.

  1. Comme d'autres l'ont dit, obj.wait()ne peut être appelé que si le thread actuel détient le verrou primitif / mutex pourobj . Si le thread actuel ne détient pas le verrou, vous obtenez l'exception que vous voyez.

  2. L' thread.wait()appel ne fait pas ce que vous semblez attendre de lui. Plus précisément, thread.wait() ne fait pas attendre le thread désigné. Cela fait plutôt attendre le thread actuel jusqu'à ce qu'un autre thread appelle thread.notify()ou thread.notifyAll().

    Il n'existe en fait aucun moyen sûr de forcer une Threadinstance à se mettre en pause si elle ne le souhaite pas. (Le plus proche de Java est la Thread.suspend()méthode obsolète , mais cette méthode est intrinsèquement dangereuse, comme expliqué dans le Javadoc.)

    Si vous voulez que le nouveau démarré Threadse mette en pause, la meilleure façon de le faire est de créer une CountdownLatchinstance et de demander au thread d'appeler await()le verrou pour se mettre en pause. Le thread principal appellerait alors countDown()le verrou pour laisser le thread mis en pause continuer.

  3. Orthogonal aux points précédents, l'utilisation d'un Threadobjet comme verrou / mutex peut poser des problèmes. Par exemple, le javadoc pour Thread::joindit:

    Cette implémentation utilise une boucle d' this.waitappels conditionnés sur this.isAlive. Lorsqu'un thread se termine, la this.notifyAllméthode est appelée. Il est recommandé que les applications utilisent pas wait, notifyou notifyAllsur les Threadinstances.


2

Puisque vous n'avez pas publié de code, nous travaillons en quelque sorte dans le noir. Quels sont les détails de l'exception?

Appelez-vous Thread.wait () à partir du thread ou de l'extérieur?

Je pose cette question car selon le javadoc pour IllegalMonitorStateException, c'est:

Lancé pour indiquer qu'un thread a tenté d'attendre sur le moniteur d'un objet ou pour notifier d'autres threads en attente sur le moniteur d'un objet sans posséder le moniteur spécifié.

Pour clarifier cette réponse, cet appel à attendre sur un thread lève également IllegalMonitorStateException, bien qu'il ait été appelé depuis un bloc synchronisé:


     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: Je pense que vous confondez le thread d'exécution et l'objet qui est la cible wait().
Robert Munteanu

@Robert - Peut-être que je le suis, mais je ne pense pas. Si vous démarrez une instance de Thread, puis demandez-lui d'attendre, vous obtiendrez une IllegalMonitorStateException, ce que j'essayais de décrire.
CPerkins

Parlez-vous de la worker.wait()ligne? Ensuite, vous devriez synchroniser sur le travailleur, pas sur le verrou.
Robert Munteanu

1

Afin de traiter l'exception IllegalMonitorStateException, vous devez vérifier que toutes les invocations des méthodes wait, notify et notifyAll ont lieu uniquement lorsque le thread appelant possède le moniteur approprié . La solution la plus simple consiste à enfermer ces appels dans des blocs synchronisés. L'objet de synchronisation qui doit être invoqué dans l'instruction synchronisée est celui dont le moniteur doit être acquis.

Voici l'exemple simple pour comprendre le concept de moniteur

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

L'appel Thread.wait () a du sens dans un code qui se synchronise sur l'objet Thread.class. Je ne pense pas que ce soit ce que tu voulais dire.
Tu demandes

Comment puis-je faire attendre un thread jusqu'à ce qu'il soit notifié?

Vous ne pouvez faire attendre que votre thread actuel. Tout autre fil ne peut être demandé d'attendre que doucement, s'il est d'accord.
Si vous voulez attendre une condition, vous avez besoin d'un objet de verrouillage - l'objet Thread.class est un très mauvais choix - c'est un AFAIK singleton donc la synchronisation dessus (sauf pour les méthodes statiques Thread) est dangereuse.
Les détails de la synchronisation et de l'attente sont déjà expliqués par Tom Hawtin. java.lang.IllegalMonitorStateExceptionsignifie que vous essayez d'attendre un objet sur lequel vous n'êtes pas synchronisé - il est illégal de le faire.


0

Je ne sais pas si cela aidera quelqu'un d'autre ou non, mais c'était l'élément clé pour résoudre mon problème dans la réponse de l'utilisateur "Tom Hawtin - tacklin" ci-dessus:

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

Juste le fait que le "verrou" est passé comme argument dans synchronized () et qu'il est également utilisé dans "lock" .notifyAll ();

Une fois que je l'ai fait dans ces 2 endroits, je l'ai fait fonctionner


0

J'ai reçu un IllegalMonitorStateExceptioncertain temps en essayant de réveiller un thread dans / d'un autre class/ thread. Dans, java 8vous pouvez utiliser les lockfonctionnalités de la nouvelle API de concurrence au lieu de synchronizedfonctions.

Je stockais déjà des objets pour asynchronousles transactions websocket dans un fichier WeakHashMap. La solution dans mon cas était également de stocker un lockobjet dans unConcurrentHashMap pour les synchronousréponses. Notez le condition.await(non .wait).

Pour gérer le multi-threading, j'ai utilisé un Executors.newCachedThreadPool()pour créer un pool de threads .


0

Ceux qui utilisent Java 7.0 ou une version inférieure peuvent se référer au code que j'ai utilisé ici et cela fonctionne.

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
    }

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.