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)
È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top