Mi profesor en una clase de Java de nivel superior sobre subprocesos dijo algo de lo que no estaba seguro.
Dijo que el siguiente código no actualizaría necesariamente la ready
variable. Según él, los dos hilos no comparten necesariamente la variable estática, específicamente en el caso en que cada hilo (hilo principal versusReaderThread
) se ejecuta en su propio procesador y, por lo tanto, no comparte los mismos registros / caché / etc. y una CPU. no actualizará el otro.
Esencialmente, dijo que es posible que ready
se actualice en el hilo principal, pero NO en el ReaderThread
, por lo queReaderThread
repetirá infinitamente.
También afirmó que era posible que el programa imprimiera 0
o 42
. Entiendo cómo 42
podría imprimirse, pero no 0
. Mencionó que este sería el caso cuando elnumber
variable se establece en el valor predeterminado.
Pensé que tal vez no se garantiza que la variable estática se actualice entre los subprocesos, pero esto me parece muy extraño para Java. ¿Hacer ready
volátiles corrige este problema?
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;
}
}
fuente
Respuestas:
No hay nada especial en las variables estáticas cuando se trata de visibilidad. Si son accesibles, cualquier hilo puede llegar a ellos, por lo que es más probable que vea problemas de concurrencia porque están más expuestos.
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 hilo hace que se hagan visibles para otros hilos de manera oportuna (en realidad, la JVM no tiene la obligación de hacer que esos cambios sean visibles para usted, en ningún marco de tiempo), a menos que establezca una relación de suceder antes .
Aquí hay una cita de ese enlace (proporcionada en el comentario de Jed Wesley-Smith):
fuente
Estaba hablando de visibilidad y no debe tomarse demasiado literalmente.
De hecho, las variables estáticas se comparten 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 cómo presentó la información:
Pero, de nuevo, esto es simplemente un modelo mental para pensar en hilos y volátiles, no literalmente cómo funciona la JVM.
fuente
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 las cachés de la CPU, sino también por la ejecución desordenada de las instrucciones.
Por lo tanto, Java define un modelo de memoria , que establece bajo qué circunstancias los subprocesos pueden ver el estado consistente de los datos compartidos.
En su caso particular, agregar
volatile
garantiza visibilidad.fuente
Por supuesto, son "compartidos" en el sentido de que ambos se refieren a la misma variable, pero no necesariamente ven las actualizaciones del otro. Esto es cierto para cualquier variable, no solo estática.
Y, en teoría, las escrituras realizadas por otro hilo pueden parecer estar en un orden diferente, a menos que se declaren las variables
volatile
o que las escrituras estén explícitamente sincronizadas.fuente
Dentro de un solo cargador de clases, los campos estáticos siempre se comparten. Para limitar explícitamente los datos a los subprocesos, querrá usar una instalación como
ThreadLocal
.fuente
Cuando inicializa la variable de tipo primitivo estático, java asigna un valor predeterminado para las variables estáticas
cuando define la variable así, 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, por lo que la variable ready cambia. para que el hilo secundario salga del ciclo while e imprima el valor. cuando se imprime, el valor inicializado del número es 0. si el proceso de subproceso ha pasado mientras el bucle antes del subproceso principal actualiza la variable de número. entonces existe la posibilidad de imprimir 0
fuente
@dontocsata puedes volver con tu maestro y educarlo un poco :)
pocas notas del mundo real y sin importar lo que veas o te digan. TENGA EN CUENTA que las palabras a continuación se refieren a este caso particular en el orden exacto que se muestra.
Las siguientes 2 variables residirán en la misma línea de caché en prácticamente cualquier arquitectura conocida.
Thread.exit
(hilo principal) está garantizado para salir yexit
se garantiza que causará una barrera de memoria, debido a la eliminación de subprocesos del grupo de subprocesos (y muchos otros problemas). (es una llamada sincronizada, y no veo una única manera de implementarlo sin la parte de sincronización, ya que ThreadGroup también debe terminar si no quedan hilos de demonio, etc.).¡El hilo iniciado
ReaderThread
mantendrá vivo el proceso ya que no es un demonio! Por lo tanto,ready
ynumber
se eliminarán juntos (o el número anterior si se produce un cambio de contexto) y no hay una razón real para reordenar en este caso, al menos ni siquiera puedo pensar en uno. Necesitarás algo realmente extraño para ver cualquier cosa menos42
. Nuevamente, 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 largo O una JVM que no los asigne en un área continua (línea de caché).fuente