2010-08-15 10 views
71

He leído "When to use 'volatile' in 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?¿Cuándo utiliza exactamente la palabra clave volátil en Java?

+0

posible duplicado de [? Por lo que se utiliza "volátil"] (http://stackoverflow.com/questions/3430757/for-what-is-used-volátil), [¿Alguna vez utilizó la palabra clave volátil en Java?] (http://stackoverflow.com/questions/106591/do-y-ever-use-the -volatile-keyword-in-java), [usando la palabra clave volátil] (http: // stackoverflow.com/questions/2644771/using-volatile-keyword) y muchos más. –

+0

http://stackoverflow.com/questions/106591/do-you-ever-use-the-volatile-keyword-in-java así como – nos

+2

* Los bloqueos ofrecen dos características principales: exclusión mutua y visibilidad. Las variables volátiles comparten las características de visibilidad de las funciones de sincronización, pero ninguna de las de atomicidad. * [Más información] (http://www.ibm.com/developerworks/library/j-jtp06197/) (por Brian Goetz). – Daniel

Respuesta

86

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 necesidad compuesto atomicidad.

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 puede usted sólo tiene que utilizar private static int temperature? De hecho, puede (en el sentido de que su programa no explotará o algo así), pero el cambio a 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 usa 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 construidos etc.).

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

EDIT:
respuesta a este comentario:

Ok, pero ¿por qué no podemos hacer todaysTemperature sincronizado y crear un captador de temperatura sincronizado?

Puede 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 ser que prefieren volatile si puedes:

  1. Menos de errores propensos: Esto depende del contexto, pero en muchos casos utilizando volatile es menos propenso a errores de concurrencia, como el bloqueo, mientras que mantiene el bloqueo, bloqueos, etc.
  2. Más rendimiento: en la mayoría de las implementaciones de JVM, volatile puede 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.
+0

He visto esta pregunta mucho, y esta es la primera respuesta que tiene perfecto sentido para mí. Gracias. – uyuyuy99

+0

Bien, pero ¿por qué no podemos sincronizar 'todaysTemperature' y crear un getter sincronizado para' temperature'? –

+3

@SemyonDanilov: Se agregó una respuesta en la parte inferior. HTH. –

2

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

"La palabra clave volátil se utiliza en las variables que se pueden modificar 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 verlos".

13

Volatile es muy útil en algoritmos sin bloqueos. Marca la variable que contiene los datos compartidos como volátil cuando no está utilizando el bloqueo 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.

5

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

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

Para más información sobre este tema, consulte Java Concurrency in Practice.

+3

Así que creo que la parte "inmutable" de esto es algo cuestionable ... vea mi respuesta aquí: http://stackoverflow.com/ preguntas/3964317/memory-barriers-and-coding-style-over-a-java-vm y las Preguntas frecuentes sobre JSR-133, artículo http://www.cs.umd.edu/~pugh/java/memoryModel/jsr- 133-faq.html # volátil. Creo que una afirmación más precisa es que puede publicar objetos de forma segura que no requieren otra sincronización ... Es posible que no me ocupe bien de esto, pero siempre que se produzca una escritura volátil, las escrituras normales en el mismo subproceso que preceden sería visible para cualquier lectura de hilo ... – andersoj

+1

@andersoj es correcto, la volatilidad es transitiva. una lectura de a.b es volátil si la lectura de a es volátil. una escritura en a.b sucede, antes de escribir a a si lo precede en orden de programa. Por lo tanto, volátil puede usarse para publicar de forma segura un objeto mutable (que de lo contrario no es apto para enhebrar). No creo que esta sea una idea particularmente buena, y siempre predico objetos inmutables siempre que sea posible, pero sigue siendo un punto importante. –

3

En realidad estoy en desacuerdo con el ejemplo dado en la respuesta más votado, según mi conocimiento lo hace NOT 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 continuar 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 programa.

Una mejor forma 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 razones de simplicidad, vamos a suponer que sólo 2 hilos se están ejecutando, hilo principal e hilo 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 dos últimas instrucciones de la tarea pueden NO ser reordenados con fines de optimización, ya sea por el compilador, 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 de volátil (en el proceso de impresión de ella),

1) Hay una garantía de que verá el más recientemente escrito valor de esta variable volátil independientemente de cuántos hilos estén escribiendo, independientemente del método en el que lo actualicen, sincronizado o no.

2) Si la declaración system.out en el hilo principal se ejecuta, después de el instante de tiempo en el que el hilo 2 se ha ejecutado la temperatura declaración = temperatura, tanto ayer de la temperatura y del día de hoy se garantizará la temperatura para imprimir los valores establecidos en ellos por el hilo 2 cuando se ejecuta la declaración de temperatura = temperatura.

Esta situación se vuelve un LOT complejo más si a) múltiples hilos se están ejecutando y b) Hay otros métodos que sólo el método setTemperatures que puede actualizar la temperatura de la temperatura y del día de hoy de ayer variable que están siendo activamente llamado por estos otros trapos. 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átil para la sincronización es extremadamente arriesgado, y sería mejor que se quedara sincronizando sus métodos.

+0

Su ejemplo también podría servir para explicar dónde podría ser mejor usar sincronizados. Esas instrucciones de impresión podrían ejecutarse mientras que el otro hilo había ejecutado "yesterdaysTemperature = yestemp" pero aún no se había ejecutado "temperature = temp", ¿no es así? – EricS

4

volatilepalabra clave 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.

De la concurrencia de Java tutorial:

Uso de variables volátiles reduce el riesgo de errores de coherencia de memoria, ya que cualquier escritura a una variable volátil establece una relación con la posterior-antes de que ocurra lee 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 su consulta:

¿Cómo sé cuando debería 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 lector siempre obtienen último valor de una variable, tiene que marcar variable como volatile

Si usted tiene un subproceso de escritura para modificar el valor de los hilos lector variables y múltiples a lea 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. Tiene que synchronize el código o usar construcciones de alto nivel concurrency como Locks, Concurrent Collections, Atomic variables etc.

SE relacionada preguntas/artículos:

Volatile variable explanation in Java docs

Difference between volatile and synchronized in Java

javarevisited artículo

Cuestiones relacionadas