¿Cuándo exactamente usa la palabra clave volátil en Java?

He leído “¿ Cuándo usar ‘volátil‘ en Java? “, Pero todavía estoy confundido. ¿Cómo puedo saber cuándo debo marcar una variable volátil? ¿Qué pasa si me equivoco, ya sea omitiendo un elemento volátil en algo que lo necesita o poniendo volátil en algo que no lo hace? ¿Cuáles son las reglas generales para determinar qué variables deberían ser volátiles en el código multiproceso?

Básicamente, utilícelo cuando desee permitir el acceso a una variable miembro mediante varios subprocesos, pero no necesita una atomicidad compuesta (no estoy seguro de si esta es la terminología correcta).

class BadExample { private volatile int counter; public void hit(){ /* This operation is in fact two operations: * 1) int tmp = this.counter; * 2) this.counter = tmp + 1; * and is thus broken (counter becomes fewer * than the accurate amount). */ counter++; } } 

lo anterior es un mal ejemplo, porque necesita una atomicidad compuesta.

  class BadExampleFixed { private int counter; public synchronized void hit(){ /* * Only one thread performs action (1), (2) at a time * "atomically", in the sense that other threads can not * observe the intermediate state between (1) and (2). * Therefore, the counter will be accurate. */ counter++; } } 

Ahora a un ejemplo válido:

  class GoodExample { private static volatile int temperature; //Called by some other thread than main public static void todaysTemperature(int temp){ // This operation is a single operation, so you // do not need compound atomicity temperature = temp; } public static void main(String[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); } } } 

Ahora, ¿por qué no puedes usar private static int temperature ? De hecho, usted puede (en el sentido de que su progtwig no explotará o algo así), pero el cambio a la temperature por el otro hilo puede o no ser “visible” para el hilo principal.

Básicamente esto significa que incluso es posible que su aplicación. sigue escribiendo Today's temperature is 0 para siempre si no se usa volatile (en la práctica, el valor tiende a ser eventualmente visible. Sin embargo, no debe arriesgarse a no usar volátiles cuando sea necesario, ya que puede provocar errores desagradables (causados ​​por objetos completamente construidos, etc.).

Si coloca palabra clave volatile en algo que no necesita volatile , no afectará la corrección de su código (es decir, el comportamiento no cambiará). En términos de rendimiento, dependerá de la implementación de JVM. En teoría, puede obtener una pequeña degradación del rendimiento porque el comstackdor no puede reorganizar las optimizaciones, tiene que invalidar el caché de la CPU, etc., pero de nuevo el comstackdor podría probar que su campo no puede accederse por múltiples hilos y eliminar el efecto de volatile palabra clave por completo y comstackrla en instrucciones idénticas.

EDITAR:
Respuesta a este comentario:

Ok, pero ¿por qué no podemos sincronizar la temperatura de hoy y crear un captador sincronizado para la temperatura?

Puedes y se comportará correctamente. Todo lo que pueda con volatile se puede hacer con synchronized , pero no al revés. Hay dos razones por las que puede preferir la volatile si puede:

  1. Menos propenso a errores: esto depende del contexto, pero en muchos casos el uso de volatile es menos propenso a los errores de concurrencia, como el locking mientras se mantiene el locking, los lockings, etc.
  2. Más rendimiento: en la mayoría de las implementaciones de JVM, los volatile pueden tener un rendimiento significativamente mayor y una mejor latencia. Sin embargo, en la mayoría de las aplicaciones, la diferencia es demasiado pequeña como para importar.

Volatile es más útil en algoritmos sin locking. Marca la variable que contiene los datos compartidos como volátil cuando no está utilizando el locking para acceder a esa variable y desea que los cambios realizados por un hilo sean visibles en otro, o si desea crear una relación “pase después” para garantizar que el cálculo sea no reordenado, una vez más, para garantizar que los cambios se vuelvan visibles en el momento apropiado.

El JMM Cookbook describe qué operaciones se pueden reordenar y cuáles no.

El volatile también se puede usar para publicar de forma segura objetos inmutables en un entorno de subprocesos múltiples.

Al declarar un campo como el public volatile ImmutableObject foo garantiza que todos los subprocesos siempre vean la referencia de instancia disponible actualmente.

Consulte Java Concurrency in Practice para obtener más información sobre ese tema.

volatile palabra clave volatile garantiza que el valor de la variable volátil siempre se leerá desde la memoria principal y no desde la memoria caché local de Thread.

Desde el tutorial de simultaneidad de Java:

El uso de variables volátiles reduce el riesgo de errores de consistencia de la memoria, ya que cualquier escritura en una variable volátil establece una relación de “pase previo” con lecturas subsecuentes de esa misma variable

Esto significa que los cambios en una variable volátil siempre son visibles para otros hilos. También significa que cuando un hilo lee una variable volátil, no solo ve el último cambio en el volátil, sino también los efectos secundarios del código que provocó el cambio.

En cuanto a tu consulta:

¿Cómo puedo saber cuándo debo marcar una variable volátil? ¿Cuáles son las reglas generales para determinar qué variables deberían ser volátiles en el código multiproceso?

Si considera que todos los hilos del lector siempre obtienen el último valor de una variable, debe marcar la variable como volatile

Si tiene un subproceso de escritor para modificar el valor de la variable y varios subprocesos del lector para leer el valor de la variable , el modificador volátil garantiza la consistencia de la memoria.

Si tiene múltiples hilos para escribir y leer variables, el modificador volatile por sí solo no garantiza la consistencia de la memoria. Tienes que synchronize el código o usar construcciones de concurrencia de alto nivel como Locks , Concurrent Collections , Atomic variables etc.

Preguntas / artículos relacionados con SE:

Explicación de variable volátil en documentos Java

Diferencia entre volátil y sincronizado en Java

artículo revisado por java

En realidad estoy en desacuerdo con el ejemplo dado en la respuesta más votado, según mi conocimiento, NO ilustra adecuadamente la semántica volátil según el modelo de memoria de Java. Volatile tiene una semántica mucho más compleja.

En el ejemplo proporcionado, el hilo principal podría seguir imprimiendo “La temperatura actual es 0” para siempre, incluso si hay otro hilo ejecutándose que se supone que debe actualizar la temperatura si ese otro hilo nunca se progtwig.

Una mejor manera de ilustrar la semántica volátil es con 2 variables.

Por simplicidad, asumiremos que la única forma de actualizar las dos variables es a través del método “setTemperatures” .

Por simplicidad, supondremos que solo se están ejecutando 2 subprocesos, el subproceso principal y el subproceso 2.

 //volatile variable private static volatile int temperature; //any other variable, could be volatile or not volatile doesnt matter. private static int yesterdaysTemperature //Called by other thread(s) public static void setTemperatures(int temp, int yestemp){ //thread updates yesterday's temperature yesterdaysTemperature = yestemp; //thread updates today's temperature. //This instruction can NOT be moved above the previous instruction for optimization. temperature = temp; } 

las últimas dos instrucciones de asignación NO se pueden reordenar con fines de optimización, ya sea por el comstackdor, el tiempo de ejecución o el hardware.

 public static void main(String[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); System.out.println("Yesterday's temperature was "+yesterdaysTemperature ); } } 

Una vez que el hilo principal lee la temperatura variable volátil (en el proceso de impresión),

1) Existe la garantía de que verá el valor escrito más reciente de esta variable volátil, independientemente de la cantidad de subprocesos que le estén escribiendo, independientemente del método en el que lo actualicen, sincronizado o no.

2) Si se ejecuta la instrucción system.out en el hilo principal, después del instante de tiempo en el que el hilo 2 ha ejecutado la statement temperature = temp, tanto la temperatura de ayer como la de hoy se garantizarán para imprimir los valores establecidos en ellos por el hilo 2 cuando ejecutó la statement temperatura = temperatura.

Esta situación se vuelve mucho más compleja si a) Se están ejecutando varios subprocesos yb) Hay otros métodos además del método de establecer temperaturas que pueden actualizar la temperatura de ayer y la temperatura de hoy que estos otros hilos están llamando activamente. Creo que tomaría un artículo de un tamaño decente para analizar las implicaciones en función de cómo el Modelo de memoria Java describe la semántica volátil.

En resumen, intentar usar volátiles para la sincronización es extremadamente arriesgado, y sería mejor que te mantuvieras sincronizando tus métodos.

http://mindprod.com/jgloss/volatile.html

“La palabra clave volátil se usa en variables que pueden ser modificadas simultáneamente por otros hilos”.

“Como otros subprocesos no pueden ver las variables locales, nunca es necesario marcar las variables locales como volátiles. Se necesita sincronizar para coordinar los cambios a las variables de diferentes subprocesos, pero a menudo volátiles solo lo hará para verlas”.

voltalie significa mantener el valor cambiante. El valor de esta variable nunca se almacenará en caché localmente: todas las lecturas y escrituras irán directamente a la “memoria principal”. En otras palabras, el comstackdor Java y el subproceso que no almacenan en caché el valor de esta variable y siempre leen desde la memoria principal.