38 votes

Pourquoi synchronized notifyAll entraîne-t-il une exception IllegalMonitorStateException?

Pourquoi ce programme de test entraîne-t-il une exception java.lang.IllegalMonitorStateException?

 public class test {
    static Integer foo = new Integer(1);
    public static void main(String[] args) {
        synchronized(foo) {
            foo++;
            foo.notifyAll();
        }
        System.err.println("Success");
    }
}
 

Résultat:

 Exception in thread "main" java.lang.IllegalMonitorStateException
        at java.lang.Object.notifyAll(Native Method)
        at test.main(test.java:6)
 

54voto

erickson Points 127945

Vous avez noté à juste titre que notifyAll doit être appelé à partir d'un bloc synchronisé.

Cependant, dans votre cas, en raison de l'auto-boxing, l'objet de la synchronisation n'est pas dans la même instance que vous avez invoqué notifyAll sur. En fait, les nouvelles, incrémenté foo instance est toujours confiné à la pile, et pas d'autres threads peuvent éventuellement être bloqué sur un wait appel.

Vous pouvez implémenter votre propre, mutable compteur sur lequel la synchronisation est effectuée. Selon votre application, vous pouvez également trouver que AtomicInteger répond à vos besoins.

3voto

dbt Points 41

Vous devez également vous méfier de verrouiller ou de notifier des objets tels que String et Integer qui peuvent être internés par la JVM (pour éviter de créer beaucoup d'objets qui représentent l'entier 1 ou la chaîne "").

3voto

Eric Leschinski Points 14289

L'incrémentation de l'entier fait disparaître l'ancien foo et le remplacer par un nouvel objet foo qui n'est pas synchronisé avec la variable foo précédente.

Voici une implémentation d'AtomicInteger suggérée ci-dessus par erickson. Dans cet exemple, foo.notifyAll (); ne produit pas d'exception java.lang.IllegalMonitorStateException car l'objet AtomicInteger n'est pas actualisé lorsque foo.incrementAndGet (); est exécuté.

 import java.util.concurrent.atomic.AtomicInteger;

public class SynchronizeOnAPrimitive {
    static AtomicInteger foo = new AtomicInteger(1);
    public static void main(String[] args) {
        synchronized (foo) {
            foo.incrementAndGet();
            foo.notifyAll();
        }
        System.out.println("foo is: " + foo);
    }
}
 

Production:

 foo is: 2
 

1voto

matt b Points 73770

Comme l'a noté erickson, le code sans l'opérateur de post-incrémentation fonctionne sans erreur:

 static Integer foo = new Integer(1);

public static void main(String[] args) {
    synchronized (foo) {
        foo.notifyAll();
    }
    System.out.println("Success");
}
 

production:

Succès

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X