java synchronization illegalmonitorstateexcep

java - ¿Qué puede causar IllegalMonitorStateException desde dentro de un bloque sincronizado?



synchronization (4)

Hoy llegamos a una excepción extremadamente sorprendente. Dentro de un bloque sincronizado, llamamos a wait () y lanza la IllegalMonitorStateException . ¿Qué puede causar esto?

Esto está ocurriendo en un código fuente abierto bien probado: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup#l222

Eliminamos las causas obvias:

  • ¿Estamos sincronizados en la variable correcta? Sí, es muxLock
  • ¿Es una variable mutable? No, muxLock es final
  • ¿estamos utilizando algún "JVM" raro que pueda afectar el comportamiento del monitor? No, pero estamos lanzando la JVM incrustada dentro de una aplicación C ++ a través de JNI.
  • ¿Es esto una JVM extraña? No, es Sun''s 1.6.0_25 win / x64 JRE
  • ¿Es este un error conocido de JVM? No puedo encontrar nada relevante en http://bugs.sun.com/bugdatabase

Por lo tanto, estoy tratando de pensar en explicaciones más descabelladas.

  • ¿Un error de falta de memoria no capturado puede provocar un error en el estado del monitor? Estamos viendo esto, pero no estamos viendo evidencia de errores de memoria todavía.

ACTUALIZACIÓN: (basado en el comentario)

También verifiqué en el seguimiento de la pila y el punto de interrupción que el subproceso está de hecho dentro del bloque sincronizado cuando se lanza la excepción. No es el caso de que algún otro código no relacionado emita la excepción (¡a menos que algo realmente confunda a Eclipse!)


La respuesta es, en mi opinión, que es un error o que alguien cambió el objeto detrás de la referencia a pesar de ser definitivo. Si puede reproducirlo, recomiendo establecer un punto de interrupción de lectura / escritura en el campo muxlock para ver si se toca o no. Puede verificar el código de identidad del muxlock en la primera línea del bloque sincronizado, y antes de esperar y notificar con entradas de registro o puntos de interrupción apropiados. Con la reflexión puedes cambiar las referencias finales. Cita de http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html :

" Si el campo subyacente es final, el método lanza una IllegalAccessException a menos que setAccessible (true) haya tenido éxito para este campo y este campo no sea estático. Establecer un campo final de esta manera es significativo solo durante la deserialización o reconstrucción de instancias de clases con los campos finales en blanco, antes de que estén disponibles para el acceso de otras partes de un programa. El uso en cualquier otro contexto puede tener efectos impredecibles, incluidos los casos en que otras partes de un programa continúen usando el valor original de este campo ".

Tal vez es un error en eclispe, y durante la depuración de alguna manera cambia el campo. ¿Es reproducible fuera de eclispe también? Ponga una huella de impresión en la captura y vea qué sucede.


Las variables de los miembros no son tan definitivas como cabría esperar. Primero debe colocar el objeto sincronizado en una variable local final. Esto no explica por qué se modifica la variable miembro, pero si soluciona el problema, al menos sabrá que la variable miembro está realmente modificada.


Lo único sospechoso que veo es que estás pasando una referencia a ''este'' a algún otro objeto en tu constructor. ¿Es posible (de hecho, no improbable) que, a través de un extraño reordenamiento de las cosas, si algún otro hilo obtiene esa referencia a ''esto'' y llama al método que usa el muxlock, las cosas pueden ir muy mal?

La especificación del lenguaje Java es bastante específica al respecto:

Se considera que un objeto está completamente inicializado cuando finaliza su constructor . Se garantiza que un subproceso que solo puede ver una referencia a un objeto después de que ese objeto se haya inicializado completamente ve los valores correctamente inicializados para los campos finales de ese objeto.

En otras palabras, si otro hilo se apodera de la referencia ''this'' antes de que el constructor finalice, es posible que el campo final ''muxlock'' aún no se haya inicializado correctamente. En general, publicar una referencia a ''esto'' antes de que el constructor haya terminado puede ser bastante peligroso, especialmente en situaciones de subprocesos.

Alguna discusión potencialmente útil sobre tales cosas: http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

Para una discusión general más antigua, pero aún útil, de por qué publicar ''esto'' en un constructor es una muy mala idea en general, consulte por ejemplo: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html


http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?r1=1069292&r2=1135026&diff_format=h

Aquí puedo ver que el tiempo de espera fue agregado últimamente

asegúrese de que startTimeout sea> mayor a 0, de lo contrario, esperará (0) o esperará (-n) esto probablemente cause IllegalMonitorStateException

EDITAR: Ok arriba es un desastre Pero vamos a intentar esto:

estamos en Mux constructor: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

la línea 176 creamos SocketChannelConnectionIO y pasamos esto después de que rompamos y un hilo diferente asume el control.

en el constructor de SocketChannelConnectionIO definido aquí: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup línea 112 registramos canalizar con el nuevo controlador ().

el controlador recieave algo en chanel y la función, digamos que la función handleReadReady se ejecuta, sincronizamos en muxLock.

¡Ahora todavía estamos en el constructor así que el objeto en el final sigue siendo mutable! supongamos que cambia, ahora tenemos algo esperando en muxLock diferente

Escenario uno en un millón

EDITAR

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?revision=1135026&view=co

Mux(SocketChannel channel, int role, int initialInboundRation, int maxFragmentSize) throws IOException { this.role = role; if ((initialInboundRation & ~0x00FFFF00) != 0) { throw new IllegalArgumentException( "illegal initial inbound ration: " + toHexString(initialInboundRation)); } this.initialInboundRation = initialInboundRation; this.maxFragmentSize = maxFragmentSize; //LINE BELOW IS CAUSING PROBLEM it passes this to SocketChannelConnectionIO this.connectionIO = new SocketChannelConnectionIO(this, channel); //Lets assume it stops here we are still in constructor //and we are not in synchronized block directBuffersUseful = true; }

ahora en el constructor de SocketChannelConnectionIO http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co

SocketChannelConnectionIO(Mux mux, SocketChannel channel) throws IOException { super(mux); channel.configureBlocking(false); this.channel = channel; //Line below we are registering to the channel with mux that is still mutable //this is the line that actually is causing the problem move that to // start() and it should work key = selectionManager.register(channel, new Handler()); }

mover este código a start () debería funcionar key = selectionManager.register(channel, new Handler()); (Supongo que el inicio es un archivo ejecutable cuando queremos comenzar a procesar)

/** * Starts processing connection data. */ void start() throws IOException { key = selectionManager.register(channel, new Handler()); key.renewInterestMask(SelectionKey.OP_READ); }

Pero sería mucho mejor no crear SocketChannelConnectionIO en el constructor de mux pero quizás en algún lugar después de eso lo mismo para el segundo constructor que cree StreamConnectionIO con este