web-dev-qa-db-fra.com

IllegalMonitorStateException sur un appel wait ()

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

155
prakash.panjwani

Vous devez être dans un bloc synchronized pour que Object.wait() fonctionne.

En outre, je vous recommande de regarder les packages de concurrence d'accès à la place des anciens packages de threading. Ils sont plus sûrs et plus faciles à utiliser .

Bonne codage.

EDIT

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

166
reccles

wait est défini dans Object et non pas Thread. Le moniteur sur Thread est 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 des diagnostics un peu plus faciles à lire, avec un faible coût en mémoire (environ 2 Ko par processus) en utilisant une classe nommée:

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

Afin de wait ou notify/notifyAll un objet, vous devez conserver le verrou avec l'instruction synchronized. En outre, vous aurez besoin d'une boucle while pour vérifier l'état de réveil (recherchez un bon texte sur le filetage pour expliquer pourquoi).

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

Prévenir:

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

Il est utile d’apprendre à comprendre à la fois le langage Java et les verrous Java.util.concurrent.locks (et Java.util.concurrent.atomic) lorsqu’on s’adonne au multithreading. Mais utilisez Java.util.concurrent structures de données chaque fois que vous le pouvez.

51

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

S'il vous plaît lire cette définition de illegMonitorException encore et encore ...

ne exception 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 encore et encore dit, IllegalMonitorException arrive quand une des 2 situations se produit ....

1> attendez 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 eu leurs réponses ... qui tous ne le font pas, alors s'il vous plaît vérifier 2 déclarations ....

synchronisé (objet)

object.wait ()

Si les deux objet sont identiques ... aucune exception illégaleMonitorException ne peut arriver.

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

24
Mina

Sur la base de vos commentaires, vous semblez faire 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 en cours détient le verrou/mutex primitif pour obj. Si le thread actuel ne tient pas le verrou, vous obtenez l'exception que vous voyez.

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

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

    Si vous voulez que le Thread nouvellement démarré marque une pause, la meilleure façon de le faire est de créer une instance CountdownLatch et de laisser l'appel de thread await() sur le loquet se mettre en pause. Le thread principal appelle ensuite countDown() sur le loquet pour laisser le thread en pause continuer.

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

    Cette implémentation utilise une boucle d'appels this.wait subordonnée à this.isAlive. Lorsqu'un thread se termine, la méthode this.notifyAll est appelée. Il est recommandé aux applications de ne pas utiliser les instances wait, notify ou notifyAll sur Thread.

5
Stephen C

Pour traiter l'exception IllegalMonitorStateException, vous devez vérifier que tous les appels des méthodes wait, notify et notifyAll ont lieu uniquement lorsque l'appelant le thread possède le moniteur approprié. La solution la plus simple consiste à inclure ces appels dans des blocs synchronisés. L'objet de synchronisation qui doit être appelé 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();
        }
    }
}
1
Rakesh Chaudhari

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

Est-ce que vous appelez Thread.wait () depuis le thread ou en dehors de celui-ci?

Je pose cette question car, selon le javadoc de IllegalMonitorStateException, il s’agit:

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é à partir d’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) { }

    }
1
CPerkins

J'ai reçu un IllegalMonitorStateException en essayant de réveiller un fil dans/à partir d'un autre class/fil. Dans Java 8, vous pouvez utiliser les lock fonctionnalités de la nouvelle API de simultanéité à la place de synchronized fonctions.

Je stockais déjà des objets pour les transactions asynchronous websocket dans un fichier WeakHashMap. La solution dans mon cas était aussi de stocker un objet lock dans un ConcurrentHashMap pour synchronous réponses. Remarque le condition.await (pas .wait).

Pour gérer le multi threading, j’utilisais une Executors.newCachedThreadPool() pour créer un pool de threads .

0
Stuart Cardall

L'appel de Thread.wait () est logique dans un code qui se synchronise sur l'objet Thread.class. Je ne pense pas que c'est ce que tu voulais dire.
Tu demandes

Comment puis-je faire en sorte qu'un fil attende jusqu'à ce qu'il soit averti?

Vous pouvez uniquement faire patienter votre thread actuel. Tout autre sujet ne peut être que doucement invité à attendre, s'il est d'accord.
Si vous voulez attendre une condition, vous avez besoin d’un objet verrou (l’objet Thread.class est un très mauvais choix). Il s’agit d’un singleton AFAIK. Par conséquent, sa synchronisation (à l’exception des méthodes statiques Thread) est dangereuse.
Tom Hawtin a déjà expliqué les détails de la synchronisation et de l'attente. Java.lang.IllegalMonitorStateException signifie que vous essayez d'attendre un objet sur lequel vous n'êtes pas synchronisé - il est illégal de le faire.

0
Tadeusz Kopec

Ceux qui utilisent Java 7.0 ou une version anté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
    }

}
0
mannedear

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" soit passé comme argument dans synchronized () et qu'il soit également utilisé dans "verrouiller" .notifyAll ();

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

0
jp093121