Perché notificationAll () genera IllegalMonitorStateException quando sincronizzato su Integer?
-
06-07-2019 - |
Domanda
Perché questo programma di test genera un 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");
}
}
Risultato:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at test.main(test.java:6)
Soluzione
Hai notato correttamente che notifyAll
deve essere chiamato da un blocco sincronizzato.
Tuttavia, nel tuo caso, a causa del boxing automatico, l'oggetto su cui hai sincronizzato non è la stessa istanza su cui hai invocato notificationAll
. In effetti, la nuova istanza foo
incrementata è ancora confinata nello stack e nessun altro thread potrebbe essere bloccato su una chiamata wait
.
È possibile implementare il proprio contatore modificabile su cui viene eseguita la sincronizzazione. A seconda della tua applicazione, potresti anche scoprire che AtomicInteger soddisfa le tue esigenze.
Altri suggerimenti
Dovresti anche essere cauto nel bloccare o notificare oggetti come String e Integer che possono essere internati dalla JVM (per impedire la creazione di molti oggetti che rappresentano l'intero 1 o la stringa " ").
L'aumento dell'intero fa scomparire il vecchio foo e lo sostituisce con un nuovissimo oggetto foo che non è sincronizzato con la precedente variabile foo.
Ecco un'implementazione di AtomicInteger che Erickson ha suggerito sopra. In questo esempio foo.notifyAll (); non produce un java.lang.IllegalMonitorStateException perché l'oggetto AtomicInteger non viene aggiornato quando foo.incrementAndGet (); è eseguito.
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);
}
}
Output:
foo is: 2
Come ha notato erickson, il codice senza l'operatore di postincremento funziona senza errori:
static Integer foo = new Integer(1);
public static void main(String[] args) {
synchronized (foo) {
foo.notifyAll();
}
System.out.println("Success");
}
uscita:
Successo