java - ¿Las variables estáticas se comparten entre hilos?
multithreading concurrency (7)
Mi profesor en una clase java de nivel superior en el hilo dijo algo de lo que no estaba seguro.
Él afirmó que el siguiente código no necesariamente actualizaría la variable ready
. Según él, los dos hilos no necesariamente comparten la variable estática, específicamente en el caso en que cada hilo (hilo principal versus ReaderThread) se ejecuta en su propio procesador y, por lo tanto, no comparte los mismos registros / caché / etc. y uno La CPU no actualizará la otra.
Básicamente, dijo que es posible que ready
esté actualizado en el hilo principal, pero NO en ReaderThread, de modo que ReaderThread se repetirá infinitamente. También afirmó que era posible que el programa imprimiera ''0'' o ''42''. Entiendo cómo se podría imprimir ''42'', pero no ''0''. Mencionó que este sería el caso cuando la variable number
se establece en el valor predeterminado.
Pensé que quizás no estaba garantizado que la variable estática se actualizara entre los hilos, pero esto me parece muy extraño para Java. ¿Hacer ready
volátil corrige este problema?
Él mostró este código:
public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } }
@dontocsata puedes volver a tu maestro y darle clases un poco :)
algunas notas del mundo real e independientemente de lo que vea o se lo cuenten. Por favor, tenga en cuenta que las palabras a continuación se refieren a este caso en particular en el orden exacto que se muestra.
Las siguientes 2 variables residirán en la misma línea de caché prácticamente en cualquier arquitectura conocida.
private static boolean ready;
private static int number;
Thread.exit
(hilo principal) está garantizado para salir y se garantiza que causará una valla de memoria, debido a la eliminación de hilo del grupo de hilos (y muchos otros problemas). (es una llamada sincronizada, y no veo una forma única de implementarla sin la parte de sincronización, ya que el ThreadGroup también debe terminar si no quedan hilos de daemon, etc.).
El hilo iniciado ReaderThread
mantendrá el proceso vivo ya que no es un daemon! Por lo tanto, ready
y el number
se vaciará juntos (o el número anterior si ocurre un cambio de contexto) y no hay una razón real para reordenar en este caso, al menos no puedo pensar en ninguno. Necesitarás algo verdaderamente extraño para ver algo más que 42
. De nuevo, supongo que ambas variables estáticas estarán en la misma línea de caché. Simplemente no puedo imaginar una línea de caché de 4 bytes de longitud O una JVM que no los asignará en un área continua (línea de caché).
Básicamente es cierto, pero en realidad el problema es más complejo. La visibilidad de los datos compartidos puede verse afectada no solo por los cachés de la CPU, sino también por la ejecución fuera de orden de las instrucciones.
Por lo tanto, Java define un Modelo de Memoria , que establece bajo qué circunstancias los hilos pueden ver el estado consistente de los datos compartidos.
En su caso particular, agregar visibilidad de garantías volatile
.
Cuando inicializa la variable de tipo primitiva estática, java default asigna un valor para variables estáticas
public static int i ;
cuando defines la variable como esta el valor predeterminado de i = 0; Es por eso que existe la posibilidad de obtener 0. Luego, el hilo principal actualiza el valor de Boolean Ready a True. ya que ready es una variable estática, el hilo principal y el otro hilo hacen referencia a la misma dirección de memoria para que la variable lista cambie. para que el hilo secundario salga del ciclo While e imprima el valor. cuando se imprime el valor inicializado, el valor del número es 0. si el proceso del hilo ha pasado mientras se realiza el ciclo antes de la variable del número de actualización del hilo principal. entonces hay una posibilidad de imprimir 0
Dentro de un solo cargador de clases, los campos estáticos siempre se comparten. Para ThreadLocal
explícitamente los datos a los hilos, querrá utilizar una instalación como ThreadLocal
.
Estaba hablando de visibilidad y no debe tomarse demasiado literalmente.
Las variables estáticas se comparten efectivamente entre subprocesos, pero los cambios realizados en un subproceso pueden no ser visibles para otro subproceso de inmediato, lo que hace que parezca que hay dos copias de la variable.
Este artículo presenta una vista que es consistente con la forma en que presentó la información:
Primero, debes entender algo sobre el modelo de memoria de Java. He luchado un poco a lo largo de los años para explicarlo brevemente y bien. A partir de hoy, la mejor manera en que puedo pensar para describirlo es si lo imaginas de esta manera:
Cada hilo en Java tiene lugar en un espacio de memoria separado (esto es claramente falso, así que tengan paciencia conmigo en este caso).
Debe usar mecanismos especiales para garantizar que la comunicación se realice entre estos hilos, como lo haría en un sistema de transmisión de mensajes.
Las escrituras de memoria que suceden en un hilo pueden "filtrarse" y ser vistas por otro hilo, pero esto de ninguna manera está garantizado. Sin comunicación explícita, no puede garantizar qué escrituras son vistas por otros hilos, o incluso el orden en que se ven.
...
Pero, de nuevo, esto es simplemente un modelo mental para pensar en enhebrar y volátil, no literalmente cómo funciona la JVM.
No existe un problema de visibilidad específico para las variables estáticas. Existe un problema de visibilidad impuesto por el modelo de memoria de la JVM. Aquí hay un artículo que habla sobre el modelo de memoria y cómo las escrituras se vuelven visibles para los hilos . No puede contar con los cambios que un subproceso hace visibles para otros subprocesos de manera oportuna (en realidad, la JVM no tiene ninguna obligación de hacer esos cambios visibles para usted), a menos que establezca una relación de pasar antes , aquí hay una cita de ese enlace (proporcionado en el comentario de Jed Wesley-Smith):
El Capítulo 17 de la Especificación de Lenguaje Java define la relación de pasar antes a las operaciones de memoria, como lecturas y escrituras de variables compartidas. Se garantiza que los resultados de un subproceso de escritura por uno sean visibles para una lectura de otro subproceso solo si la operación de escritura ocurre, antes de la operación de lectura. Las construcciones sincronizadas y volátiles, así como los métodos Thread.start () y Thread.join (), pueden formar relaciones de pase previo. En particular:
Cada acción en un hilo ocurre, antes de cada acción en ese hilo que viene más tarde en el orden del programa.
Se produce un desbloqueo (bloqueo sincronizado o salida de método) de un monitor, antes de cada bloqueo posterior (bloqueo sincronizado o entrada de método) de ese mismo monitor. Y dado que la relación de pasar antes es transitiva, todas las acciones de un hilo antes de desbloquear suceden, antes de todas las acciones posteriores a cualquier bloqueo de hilo que monitorice.
Se produce una escritura en un campo volátil, antes de cada lectura posterior de ese mismo campo. Las escrituras y lecturas de campos volátiles tienen efectos de coherencia de memoria similares a los de entrada y salida de monitores, pero no implican el bloqueo de exclusión mutua.
Se produce una llamada para iniciar un hilo, antes de cualquier acción en el hilo iniciado.
Todas las acciones en un hilo ocurren, antes de que cualquier otro hilo vuelva exitosamente de una unión en ese hilo.
Por supuesto, se "comparten" en el sentido de que ambos se refieren a la misma variable, pero no necesariamente se ven las actualizaciones de los demás. Esto es cierto para cualquier variable, no solo estática.
Y, en teoría, las escrituras hechas por otro hilo pueden aparecer en un orden diferente, a menos que las variables sean declaradas volatile
o las escrituras estén explícitamente sincronizadas.