2008-11-03 10 views
36

¿Por qué este programa de prueba da como resultado un java.lang.IllegalMonitorStateException?¿Por qué notifyAll() genera IllegalMonitorStateException cuando se sincroniza en Integer?

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

Resultado:

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

Respuesta

51

Usted ha señalado correctamente que notifyAll debe ser llamado desde un bloque sincronizado.

Sin embargo, en su caso, debido al auto-boxing, el objeto sincronizado no es la misma instancia en la que invocó notifyAll. De hecho, la instancia nueva e incrementada de foo todavía está confinada a la pila, y no es posible bloquear ninguna otra cadena en una llamada wait.

Puede implementar su propio contador mutable en el que se realice la sincronización. Dependiendo de su aplicación, también puede encontrar que AtomicInteger satisface sus necesidades.

+0

que no se dieron cuenta de que incrementar el número entero asignaría un nuevo objeto en lugar de cambiar el valor del objeto existente. – jjvainio

+2

Esta es una de las muchas opciones de Autoboxing/unboxing. –

+1

Intentar esperar/notificar usando un Enum también puede llevar a esta condición: 'synchronized (myEnum) {myEnum = MyEnum.NEW_VALUE; myEnum.notify(); } ' – dmitrii

1

Como ha señalado Erickson, el código sin que el operador postincremento funciona sin error:

static Integer foo = new Integer(1); 

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

de salida:

Success

3

También debe ser recelosos de bloqueo o notificar sobre objetos como cuerdas y Entero que puede ser internado por la JVM (para evitar la creación de muchos objetos que representan el número entero 1 o la cadena "").

2

Al aumentar el número entero, el antiguo foo desaparece y se reemplaza por un nuevo objeto foo que no está sincronizado con la variable foo anterior.

Aquí hay una implementación de AtomicInteger que erickson sugirió anteriormente. En este ejemplo, foo.notifyAll(); no produce una excepción java.lang.IllegalMonitorStateException porque el objeto AtomicInteger no se actualiza cuando foo.incrementAndGet(); se ejecuta.

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); 
    } 
} 

Salida:

foo is: 2 
Cuestiones relacionadas