2010-08-30 13 views
34

Estoy realmente confundido acerca de lo que leí sobre las aplicaciones de palabras clave volátiles en Java.Palabra clave volátil en Java - Clarificación

  1. ¿Es correcta la siguiente declaración? "una escritura en un campo volátil ocurre antes de cada lectura posterior del mismo campo"

  2. Idealmente, ¿cuándo se debe usar la palabra clave volátil?

  3. ¿Cuál es la diferencia entre:

    class TestClass 
    { private int x; 
    
        synchronized int get(){return x;} 
        synchronized void set(int x){this.x = x;} 
    
    } 
    

y

class TestClass 
{ private volatile int x; 

    int get(){return x;} 
    void set(int x){this.x = x;} 

} 
+0

Para aclarar mi comentario duplicado automático "Posible": Las dos sugerencias anteriores no eran duplicados exactos; el mio es. Es importante que esta pregunta se resuelva como un duplicado ya que la respuesta seleccionada a esta pregunta es peligrosamente incorrecta al afirmar que la volatilidad solo afecta al campo declarado volátil; de hecho, una escritura y una lectura de cualquier variable volátil introduce una barrera de memoria completa. al igual que la sincronización entre dos hilos. –

Respuesta

57

volátil es un modificador campo, mientras sincronizado modifica bloques de código y métodos. Así que podemos especificar tres variaciones de un sencillo de acceso usando las dos palabras clave:

 int i1; 
    int geti1() {return i1;} 

    volatile int i2; 
    int geti2() {return i2;} 

    int i3; 
    synchronized int geti3() {return i3;} 

geti1() accede al valor almacenado actualmente en i1 en el hilo actual. Los subprocesos pueden tener copias locales de variables, y los datos no tienen que ser los mismos que los datos guardados en otros subprocesos. En particular, otro subproceso puede haber actualizado i1 en su subproceso, pero el valor en el subproceso actual podría ser diferente de ese valor actualizado. De hecho, Java tiene la idea de una memoria "principal", y esta es la memoria que contiene el valor "correcto" actual para las variables. Los subprocesos pueden tener su propia copia de datos para las variables, y la copia del subproceso puede ser diferente de la memoria "principal". Así que, de hecho, es posible que la memoria "principal" para tener un valor de para i1, por Thread1 tener un valor de para i1 y para thread2 tener un valor de de i1 if thread1 y thread2 han actualizado i1 pero el valor actualizado aún no se ha propagado a la memoria "principal" u otros hilos.

Por otro lado, geti2() accede de manera efectiva al valor de i2 desde la memoria "principal". No se permite que una variable volátil tenga una copia local de una variable que sea diferente del valor actualmente almacenado en la memoria "principal". Efectivamente, una variable declarada volátil debe tener sus datos sincronizados en todos los subprocesos, de modo que siempre que acceda o actualice la variable en cualquier subproceso, todos los demás subprocesos vean inmediatamente el mismo valor. Las variables generalmente volátiles tienen una mayor sobrecarga de acceso y actualización que las variables "simples". Generalmente, los hilos tienen su propia copia de datos para una mejor eficacia.

Hay dos diferencias entre volitivo y sincronizado.

Primero sincronizado obtiene y libera bloqueos en monitores que pueden forzar solo un hilo a la vez para ejecutar un bloque de código. Ese es el aspecto bastante conocido para sincronizar. Pero sincronizado también sincroniza la memoria. De hecho sincronizado sincroniza toda la memoria del hilo con la memoria "principal". Entonces ejecutando geti3() hace lo siguiente:

  1. El hilo adquiere el bloqueo en el monitor para el objeto esto.
  2. La memoria de hilo vacía todas sus variables, es decir, tiene todas sus variables efectivamente leídas de la memoria "principal".
  3. Se ejecuta el bloque de código (en este caso, se establece el valor de retorno en el valor actual de i3, que puede haberse reiniciado desde la memoria "principal").
  4. (Cualquier cambio en las variables normalmente se escribiría ahora en la memoria "principal", pero para geti3() no tenemos cambios.)
  5. El hilo libera el bloqueo en el monitor para el objeto this.

Entonces, ¿dónde volátil sólo sincroniza el valor de una variable entre la memoria de rosca y la memoria "principal", sincronizado sincroniza el valor de todas las variables entre la memoria de rosca y la memoria "principal", y se bloquea y libera un monitor para arrancar. Claramente sincronizado es probable que tenga más sobrecarga que volátil.

http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html

+1

La afirmación sobre la sincronización de una sola variable aquí no es cierta y, en general, todo lo que se escribe no suena del todo correcto. – Stas

+3

La afirmación sobre la sincronización de una sola variable aquí no es cierta y, en general, todo lo que se escribe no suena del todo correcto. Escribir en volátil tiene el mismo efecto de memoria que el lanzamiento del monitor. Leer de lo volátil tiene el mismo efecto de memoria, como monitor adquirir acción. Todo eso significa que el acceso a la variable volátil también afecta otras variables. Además, no hay nada como el bloqueo en una variable, mencionado en "Flujo volátil", hay escrituras en la memoria y la invalidación de la memoria caché. – Stas

+0

Buen enlace está aquí - http://www.cs.umd.edu/~pugh/java/memoryModel/ y -1 para el desorden con los fundamentos :) – Stas

6

volatile garantiza que se lee de la variable refleja siempre la más actualizada para actualizar el valor. El tiempo de ejecución puede lograr esto de varias formas, incluido el almacenamiento en caché o la actualización de la memoria caché cuando el valor ha cambiado.

0

Desde el punto de vista del cliente, un campo volátil privado se oculta de la interfaz pública mientras que los métodos sincronizados son más visibles.

+0

sincronizado no es parte de la interfaz pública o firma de método. –

+0

Soy corregido. De hecho, es un detalle de implementación. – gawi

2

bwawok eludió a él, pero el isnt palabra clave volátil sólo para la visibilidad de memoria. Antes de que se lanzara Java 1.5, la palabra clave volátil declaraba que el campo obtendría el valor más reciente del objeto presionando la memoria principal cada vez para las lecturas y el enjuague para las escrituras.

dos cosas muy importantes de la palabra clave volátil de hoy syas:

  1. No se preocupe acerca de cómo, pero sabemos que cuando se lee un campo volátil que siempre tendrá la mayoría hasta la fecha valor.
  2. Un compilador no puede ordenar una lectura/escritura volátil para mantener el orden del programa.
0

Para responder a la parte 3 de su pregunta, y en parte la parte 2.

No hay funcional diferencia entre synchronized y volatile muestras.

Sin embargo, cada uno tiene sus propios inconvenientes en términos de rendimiento. En algunos casos, el rendimiento de volatile puede ser realmente peor que simplemente usar synchronized u otras primitivas de java.util.concurrent. Para la discusión de esto vea ->Why aren't variables in Java volatile by default?.

0

Respuesta de Kerem Baydoğan tiene toda la razón. Solo quiero dar un ejemplo práctico sobre lo que nos ofrece la palabra clave volatile.

En primer lugar, tenemos un contador, SMTH como

public class Counter { 
    private int x; 
    public int getX() { return x; } 
    public void increment() { x++; } 
} 

Y algunas tareas ejecutables que se incrementa el valor de x

@Override 
public void run() { 
    for (N) { 
      int oldValue = counter.getX(); 
      counter.increment(); 
      int new value = counter.getX(); 
     } 

    } 
} 

Con ninguna sincronización no va a ser interference between threads y simplemente no va a funcionar

la manera más simple de resolver esto:

public class Counter { 
    private int x; 
    public synchronized int getX() { return x; } 
    public synchronized void increment() { x++; } 
} 

realidad con el fin de forzar al sistema a romper, hago un Thread.sleep antes de leer y escribir x, imagínese es un BD o una enorme tarea de tratar.


Ahora, ¿para qué sirve volatile? Hay un montón de buenos artículos de allí: volatile article o this question

sincronizar el acceso al recurso común es no la respuesta, pero es una buena opción para mantenga la bandera para detener las discusiones

que nuestra anterior Por ejemplo, imagina que queremos incrementar la variable hasta 100, de manera simple podría ser una bandera volatile boolean. Ejemplo:

private volatile boolean stop; 

@Override 
public void run() { 
    while(!stop) { 
     int oldValue = counter.getX(); 
     if (oldValue == 100) { 
      stop = true; 
     } else { 
      counter.increment(); 
      int new value = counter.getX(); 
     }  
    } 
} 

Esto funciona bien, pero, si se quita la palabra clave volatile de la bandera, es posible encontrar y bucle infinito.

Cuestiones relacionadas