Variable volátil en Java

Así que estoy leyendo este libro titulado Concurrencia de Java en la práctica y estoy atascado en esta única explicación que no puedo comprender sin un ejemplo. Esta es la cita:

Cuando el hilo A escribe en una variable volátil y posteriormente el hilo B lee esa misma variable, los valores de todas las variables que fueron visibles para A antes de escribir en la variable volátil se vuelven visibles para B después de leer la variable volátil.

¿Puede alguien darme un contraejemplo de por qué “los valores de TODAS las variables que fueron visibles para A antes de escribir en la variable volátil se vuelven visibles para B DESPUÉS de leer la variable volátil”?

Estoy confundido por qué todas las demás variables no volátiles no se vuelven visibles para B antes de leer la variable volátil.

El subproceso B puede tener un caché local de CPU de esas variables. Una lectura de una variable volátil asegura que se observe cualquier descarga de caché intermedia desde una escritura previa a la volátil.

Para ver un ejemplo, lea el siguiente enlace, que concluye con “Fijar el locking de doble check con Volatile”:

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Declarar una variable Java volátil significa:

  • El valor de esta variable nunca se almacenará en caché localmente: todas las lecturas y escrituras irán directamente a la “memoria principal”.
  • El acceso a la variable actúa como si estuviera encerrado en un bloque sincronizado, sincronizado sobre sí mismo.

Solo para su referencia, ¿Cuándo es necesario volátil?

Cuando hay varios subprocesos que usan la misma variable, cada subproceso tendrá su propia copia del caché local para esa variable. Por lo tanto, cuando se actualiza el valor, en realidad se actualiza en la memoria caché local no en la memoria variable principal. El otro hilo que está usando la misma variable no sabe nada sobre los valores cambiados por el otro hilo. Para evitar este problema, si declara una variable como volátil, no se almacenará en la memoria caché local. Cada vez que el hilo está actualizando los valores, se actualiza a la memoria principal. Por lo tanto, otros subprocesos pueden acceder al valor actualizado.

De JLS §17.4.7 Ejecuciones bien formadas

Solo consideramos ejecuciones bien formadas. Una ejecución E =

está bien formada si las siguientes condiciones son verdaderas:

  1. Cada lectura ve una escritura en la misma variable en la ejecución. Todas las lecturas y escrituras de variables volátiles son acciones volátiles. Para todo lo que lee r en A, tenemos W (r) en A y W (r) .v = rv La variable rv es volátil si y solo si r es una lectura volátil, y la variable wv es volátil si y solo si w es una escritura volátil.

  2. Ocurre antes de que el orden sea una orden parcial. Ocurre, antes de que el orden esté dado por el cierre transitivo de sincronizaciones, con los bordes y el orden del progtwig. Debe ser un orden parcial válido: reflexivo, transitivo y antisimétrico.

  3. La ejecución obedece a la coherencia dentro del hilo. Para cada hilo t, las acciones realizadas por t en A son las mismas que generaría ese hilo en orden de progtwig aislado, con cada escritura escribiendo el valor V (w), dado que cada lectura r ve el valor V ( W (r)). Los valores vistos por cada lectura están determinados por el modelo de memoria. El orden del progtwig dado debe reflejar el orden del progtwig en el cual las acciones se realizarían de acuerdo con la semántica intra-hilo de P.

  4. La ejecución ocurre antes de que sea consistente (§17.4.6).

  5. La ejecución obedece a la consistencia del orden de sincronización. Para todas las lecturas volátiles r en A, no es el caso que tampoco lo sea (r, W (r)) o que exista una ganancia de escritura A tal que wv = rv y así (W (r), w) y así ( w, r).

Enlace útil: ¿Qué sabemos realmente sobre la concurrencia sin locking en Java?

Si una variable no es volátil, entonces el comstackdor y la CPU pueden reordenar las instrucciones libremente según lo consideren oportuno para optimizar el rendimiento.

Si la variable ahora se declara volátil, entonces el comstackdor ya no intenta optimizar los accesos (lecturas y escrituras) a esa variable. Sin embargo, puede continuar optimizando el acceso para otras variables.

En el tiempo de ejecución, cuando se accede a una variable volátil, la JVM genera instrucciones de barrera de memoria apropiadas para la CPU. La barrera de la memoria tiene el mismo objective: la CPU también evita las instrucciones de reordenamiento.

Cuando se escribe una variable volátil (por el hilo A), todas las escrituras en cualquier otra variable se completan (o al menos aparecerán) y se hacen visibles para A antes de escribir en la variable volátil; esto a menudo se debe a una instrucción de barrera de escritura de memoria. Del mismo modo, cualquier lectura sobre otras variables, se completará (o aparecerá) antes de la lectura (por el hilo B); esto a menudo se debe a una instrucción de barrera de lectura de memoria. Esta ordenación de las instrucciones impuesta por la (s) barrera (s) significará que todas las escrituras visibles para A serán visibles B. Esto, sin embargo, no significa que no haya ocurrido ningún reordenamiento de las instrucciones (el comstackdor puede haber realizado reordenamiento para otras instrucciones); simplemente significa que si se ha producido alguna escritura visible para A, sería visible para B. En términos más simples, significa que no se mantiene el orden estricto del progtwig.

Señalaré este informe sobre Barreras de memoria y Concurrencia de JVM , si desea comprender cómo la JVM emite instrucciones de barrera de memoria, con más detalle.

Preguntas relacionadas

  1. ¿Qué es una valla de memoria?
  2. ¿Cuáles son algunos trucos que hace un procesador para optimizar el código?

Se permite que los subprocesos guarden en caché los valores de las variables que otros subprocesos pueden tener desde que los leyeron. La palabra clave volatile obliga a todos los subprocesos a no valores de caché.

Esto es simplemente una bonificación adicional que le otorga el modelo de memoria, si trabaja con variables volátiles.

Normalmente (es decir, en ausencia de variables volátiles y sincronización), la máquina virtual puede hacer que las variables de un hilo sean visibles para otros hilos en el orden que desee, o no lo haga en absoluto. Por ejemplo, el hilo de lectura podría leer alguna mezcla de versiones anteriores de otras asignaciones de variables de hilos. Esto se debe a que los subprocesos se pueden ejecutar en diferentes CPU con sus propios cachés, que a veces solo se copian en la “memoria principal” y, además, mediante el reordenamiento del código para fines de optimización.

Si usó una variable volátil, tan pronto como el hilo B lea algún valor X de él, la VM se asegura de que cualquier cosa que el hilo A haya escrito antes de escribir X también sea visible para B. (Y también todo lo que tiene A garantizado como visible transitivo).

Se dan garantías similares para bloques sincronizados y otros tipos de lockings.