¿Cómo demostrar los problemas de visibilidad de multiprocesamiento de Java?

Si se accede a las variables en Java desde múltiples hilos, se debe asegurar que estén publicadas de forma segura. Esto generalmente significa usar synchronized o volatile .

Tengo la impresión de que algunos de mis colegas no toman en serio este asunto, ya que “nunca antes habían escuchado hablar de volatile y sus progtwigs han funcionado durante años”.

Entonces mi pregunta es:

¿Puede alguien proporcionar un ejemplo de progtwig / fragmento de Java que muestre de manera confiable los problemas de visibilidad de los datos?

Creo que ejecutar un progtwig y ver el NPE inesperado o el valor de la variable obsoleta ayudaría más, que solo explicaciones teóricas, que no se puede demostrar.

¡Muchas gracias por tu ayuda!

Actualización: solo para enfatizar el punto nuevamente. He leído Java Concurreny en Practice y conozco ejemplos que teóricamente tienen problemas de visibilidad. Lo que estoy buscando es una forma de demostrarlos . No estoy seguro de que esto sea realmente posible, pero tal vez haya una configuración jvm o algo similar que lo permita.

    Al modificar el ejemplo aquí al eliminar operaciones, he encontrado un ejemplo que falla sistemáticamente en mi entorno (el hilo nunca deja de ejecutarse).

     // Java environment: // java version "1.6.0_0" // OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu3) // OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode) public class Test2 extends Thread { boolean keepRunning = true; public static void main(String[] args) throws InterruptedException { Test2 t = new Test2(); t.start(); Thread.sleep(1000); t.keepRunning = false; System.out.println(System.currentTimeMillis() + ": keepRunning is false"); } public void run() { while (keepRunning) {} } } 

    Tenga en cuenta que este tipo de problemas dependen bastante del comstackdor / tiempo de ejecución / sistema. En particular, el comstackdor puede determinar agregar instrucciones para leer la variable de la memoria, incluso si no es volátil, por lo que el código funcionaría, la VM y JIT pueden optimizar las lecturas de la memoria y usar solo registros, e incluso el el procesador puede reordenar las instrucciones, lo que no afectaría a este caso, pero en otros casos multiproceso, puede afectar el estado percibido de otros hilos si se modifican más de una variable.

    ¿Puede alguien proporcionar un ejemplo de progtwig / fragmento de Java que muestre de manera confiable los problemas de visibilidad de los datos?

    No, no hay ningún ejemplo fiable que muestre problemas de visibilidad de datos.

    La razón es que cualquier ejecución válida de un progtwig con volatile también es una ejecución válida del mismo progtwig sin volatile . (¡Sin embargo, lo opuesto obviamente no es cierto!)

    El ejemplo más común while(keepRunning) la importancia de usar volátiles es el ejemplo while(keepRunning) :

     public class Test extends Thread { boolean keepRunning = true; public static void main(String[] args) throws InterruptedException { Test t = new Test(); t.start(); Thread.sleep(1000); t.keepRunning = false; System.out.println(System.currentTimeMillis() + ": keepRunning is false"); } public void run() { while (keepRunning) System.out.println(System.currentTimeMillis() + ": " + keepRunning); } } 

    Dado que keepRunning puede (válidamente) mantenerse en la memoria caché del hilo que ejecuta el ciclo while, este progtwig puede imprimir “verdadero” para keepRunning en keepRunning mucho después de que keepRunning se establezca en falso.

    Sin embargo , tenga en cuenta que no existe una forma confiable de exponer las condiciones de carrera. (Vea mi otra respuesta.) Este ejemplo puede exponerlo bajo ciertas circunstancias en ciertas combinaciones de hardware / sistema operativo / jvm.

    Haga que lean el libro Java Concurrency in Practice del gurú de la concurrencia de Java Brian Goetz. ¡Ese libro es una lectura obligada para cualquiera que tenga que escribir cualquier software concurrente serio en Java!

    Por supuesto, decir “nunca he oído hablar de volatile y mis progtwigs han funcionado durante años” es un argumento tonto de la ignorancia .

    Tengo un fragmento de código para ti:

     package test; public class LoopTest { private boolean done = false; /** * @param args */ public void start() { System.out.println(System.getProperty("java.vm.name")); System.out.println(System.getProperty("java.version")); for (int i = 0; i < 100; i++) { startNewThread(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } done = true; System.out.println("forcing end"); } private void startNewThread() { new Thread(new Runnable() { public void run() { long i = 0; while(!done) { i++; if(i % 100L == 0) { System.out.println("still working " + i); } } System.out.println("ending " + i); } }).start(); } public static void main(String[] args) { new LoopTest().start(); } } 

    Este ejemplo ejecutado en el modo Servidor JVM generó esta salida en mi máquina:

     .. .. ending 14100 still working 14800 ending 14800 still working 26500 ending 26500 still working 18200 ending 18200 still working 9400 ending 9400 still working 1300 ending 1300 still working 59500 ending 59500 still working 1700 still working 75400 ending 75400 still working 33500 ending 33500 still working 36100 ending 36100 still working 121000 ending 121000 still working 3000 ending 3000 ending 1700 still working 5900 ending 5900 still working 7800 ending 7800 still working 7800 ending 7800 still working 6800 ending 6800 still working 5300 ending 5300 still working 9100 still working 10600 ending 10600 still working 9600 ending 9600 still working 10000 ending 10000 ending 9100 still working 1700 ending 1700 .. .. 

    Mire las declaraciones de "finalización #": todas ellas tienen un número que es un múltiplo de 100, lo que es poco probable que ocurra, creo. Mi interpretación es que hay un problema de visibilidad que causa que los hilos se sigan leyendo done == false aunque ya se haya actualizado a verdadero. Después de la llamada al método Synchronized System.out.println () con la instrucción "still working #", los hilos leen el valor actualizado de "done" y "exit".

    ¿O alguien ve un error en mi código / interpretación?

     public class NoVisibility { private static boolean ready = false; private static int number; private static class ReaderThread extends Thread { @Override public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) throws InterruptedException { new ReaderThread().start(); number = 42; Thread.sleep(20000); ready = true; } } 

    Coloque la llamada Thread.sleep() durante 20 segundos, lo que sucederá es que JIT se activará durante esos 20 segundos y optimizará la comprobación y el valor en caché o eliminará la condición por completo. Y entonces el código fallará en la visibilidad.

    Para evitar que esto suceda, DEBE usar volatile .

    Una extensión del código de @David (configuración Java6 64bit, Eclipse Juno SR2):

     public class NoVisibility_Demonstration extends Thread { boolean keepRunning = true; public static void main(String[] args) throws InterruptedException { NoVisibility_Demonstration t = new NoVisibility_Demonstration(); t.start(); Thread.sleep(1000); t.keepRunning = false; System.out.println(System.currentTimeMillis() + ": keepRunning is false"); } public void run() { int x = 10; while (keepRunning) { //System.out.println("If you uncomment this line, the code will work without the visibility issue"); x++; } System.out.println("x:"+x); } } 

    Usando este ejemplo, puede mostrar ambos escenarios. Cuando descomenta la línea en el ciclo while en run (), se resuelve el problema de visibilidad. La razón es que las instrucciones println () usan sincronización. Discusión detallada AQUÍ