Pregunta ¿Alguna vez usa la palabra clave volátil en Java?


En el trabajo de hoy, me encontré con el volatile palabra clave en Java. Al no estar muy familiarizado con esto, encontré esta explicación:

Teoría y práctica de Java: gestión de la volatilidad

Dado el detalle en que ese artículo explica la palabra clave en cuestión, ¿alguna vez la usa o podría ver un caso en el que pudiera usar esta palabra clave de la manera correcta?


516
2017-09-20 00:41


origen


Respuestas:


volatile tiene semántica para la visibilidad de la memoria. Básicamente, el valor de una volatile el campo se vuelve visible para todos los lectores (otros hilos en particular) después de que una operación de escritura termina en él. Sin volatile, los lectores podrían ver algún valor no actualizado.

Para responder a su pregunta: Sí, uso un volatile variable para controlar si algún código continúa un bucle. El lazo prueba el volatile valor y continúa si es true. La condición se puede configurar para false llamando a un método de "detención". El lazo ve false y termina cuando prueba el valor después de que el método de detención complete la ejecución.

El libro "Concurrencia de Java en la práctica, "que recomiendo mucho, da una buena explicación de volatile. Este libro está escrito por la misma persona que escribió el artículo de IBM al que se hace referencia en la pregunta (de hecho, cita su libro en la parte inferior de ese artículo). Mi uso de volatile es lo que su artículo llama la "bandera de estado del patrón 1".

Si quieres saber más sobre cómo volatile trabaja bajo el capó, lee en el modelo de memoria de Java. Si quieres ir más allá de ese nivel, echa un vistazo a un buen libro de arquitectura de computadora como Hennessy y Patterson y lea sobre la coherencia del caché y la coherencia del caché.


616
2017-09-20 02:09



"... el modificador volátil garantiza que cualquier hilo que lea un campo verá el valor escrito más reciente".  - Josh Bloch

Si estás pensando en usar volatile, lee en el paquete java.util.concurrent que trata con el comportamiento atómico.

La publicación de Wikipedia en una Patrón Singleton muestra uso volátil


144
2017-12-18 21:52



Punto importante sobre volatile: 

  1. La sincronización en Java es posible mediante el uso de palabras clave de Java synchronized y volatile y cerraduras
  2. En Java, no podemos tener synchronized variable. Utilizando synchronized la palabra clave con una variable es ilegal y dará como resultado un error de compilación. En lugar de usar el synchronized variable en Java, puedes usar el java volatile variable, que indicará a los subprocesos de JVM que lean el valor de volatile variable de la memoria principal y no lo almacena en la memoria caché localmente.
  3. Si una variable no se comparte entre varios hilos, entonces no hay necesidad de usar el volatile palabra clave.

fuente

Ejemplo de uso de volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

Estamos creando instancias de forma perezosa en el momento en que llega la primera solicitud.

Si no hacemos el _instance variable volatile entonces el hilo que está creando la instancia de Singleton no puede comunicarse con el otro hilo. Entonces, si el Tema A está creando una instancia de Singleton y justo después de la creación, la CPU corrompe, etc., todos los demás hilos no podrán ver el valor de _instancecomo no nulo y creerán que todavía se le asigna nulo.

¿Por qué pasó esto? Debido a que los hilos del lector no están bloqueando y hasta que el hilo del escritor sale de un bloque sincronizado, la memoria no se sincronizará y el valor de _instance no se actualizará en la memoria principal. Con la palabra clave Volátil en Java, esto es manejado por Java y todas las actualizaciones serán visibles por todos los hilos del lector.

Conclusión: volatile la palabra clave también se usa para comunicar el contenido de la memoria entre subprocesos.

Ejemplo de uso de sin volátil:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

El código anterior no es seguro para subprocesos. Aunque comprueba el valor de instancia una vez más dentro del bloque sincronizado (por razones de rendimiento), el compilador JIT puede reordenar el bytecode de manera que la referencia a la instancia se establece antes de que el constructor haya terminado su ejecución. Esto significa que el método getInstance () devuelve un objeto que puede no haberse inicializado por completo. Para que el código sea seguro para la ejecución de subprocesos, la palabra clave volátil se puede usar desde Java 5 para la variable de instancia. Las variables marcadas como volátiles solo son visibles para otros hilos una vez que el constructor del objeto ha terminado su ejecución por completo.
Fuente

enter image description here

volatile uso en Java:

Los iteradores a prueba de fallas son típicamente implementado usando un volatile contador en el objeto de la lista.

  • Cuando la lista se actualiza, el contador se incrementa.
  • Cuando un Iterator se crea, el valor actual del contador está incrustado en Iterator objeto.
  • Cuando un Iterator operación se realiza, el método compara los dos valores del contador y arroja un ConcurrentModificationException si son diferentes

La implementación de iteradores a prueba de fallas generalmente es liviana. Por lo general, dependen de las propiedades de las estructuras de datos de la implementación de la lista específica. No hay un patrón general


75
2017-09-24 22:29



volátil es muy útil para detener los hilos.

No es que debas escribir tus propios hilos, Java 1.6 tiene una gran cantidad de buenos grupos de hilos. Pero si está seguro de que necesita un hilo, deberá saber cómo detenerlo.

El patrón que uso para hilos es:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

Observe cómo no hay necesidad de sincronización


44
2017-09-20 04:00



Un ejemplo común para usar volatile es usar un volatile boolean variable como una bandera para terminar un hilo. Si ha iniciado un hilo y desea poder interrumpirlo de manera segura desde un hilo diferente, puede hacer que el hilo revise periódicamente un indicador. Para detenerlo, establezca la bandera en verdadero. Al hacer la bandera volatile, puede asegurarse de que el hilo que lo está comprobando verá que se ha configurado la próxima vez que lo comprueba sin tener que usar siquiera un synchronized bloquear.


28
2017-09-20 04:26



Sí, se debe usar volátil siempre que desee que varios subprocesos accedan a una variable mutable. No es un uso común muy común porque normalmente necesita realizar más de una operación atómica individual (por ejemplo, verificar el estado de la variable antes de modificarla), en cuyo caso utilizaría un bloque sincronizado.


12
2018-02-11 14:51



Nadie ha mencionado el tratamiento de la operación de lectura y escritura para el tipo de variable larga y doble. Las lecturas y escrituras son operaciones atómicas para variables de referencia y para la mayoría de las variables primitivas, excepto para los tipos de variables largas y dobles, que deben usar la palabra clave volátil para ser operaciones atómicas. @enlazar


12
2018-02-25 09:17



IMO dos escenarios importantes distintos de detener el hilo en el que se utiliza la palabra clave volátil

  1. Mecanismo de bloqueo verificado. Utilizado a menudo en el diseño de Singleton patrón. En esto el singleton object needs to be declared volatile.
  2. Spurious Wakeups. A veces, el subproceso se activa desde la llamada de espera incluso si no se ha emitido una llamada de notificación. Este comportamiento se llama despertar supurante. Esto se puede contrarrestar mediante el uso de una variable condicional (bandera booleana). Ponga la llamada wait () en un ciclo while siempre que la bandera sea verdadera. Entonces, si el hilo se despierta de la llamada de espera debido a cualquier otra razón que no sea notify / notifyall, entonces se encuentra con que el indicador sigue siendo verdadero y, por lo tanto, las llamadas vuelven a esperar. Antes de llamar a notificación, establezca esta marca en verdadero. En este caso, el boolean flag is declared as volatile.

9
2017-09-20 07:14



Tendrá que utilizar la palabra clave 'volátil' o 'sincronizada' y cualquier otra herramienta y técnica de control de concurrencia que pueda tener a su disposición si está desarrollando una aplicación multiproceso. Ejemplo de dicha aplicación son las aplicaciones de escritorio.

Si está desarrollando una aplicación que se implementaría en el servidor de aplicaciones (Tomcat, JBoss AS, Glassfish, etc.), no tiene que manejar usted mismo el control de simultaneidad, como ya lo ha indicado el servidor de aplicaciones. De hecho, si recuerdo correctamente, el estándar Java EE prohíbe cualquier control de concurrencia en servlets y EJB, ya que es parte de la capa de 'infraestructura' que supuestamente se liberaría de su manejo. Solo hace control de concurrencia en dicha aplicación si está implementando objetos singleton. Esto ya se abordó si tejes tus componentes usando frameworkd como Spring.

Por lo tanto, en la mayoría de los casos de desarrollo de Java donde la aplicación es una aplicación web y utiliza un marco de IoC como Spring o EJB, no necesitaría usar 'volátil'.


5
2018-03-02 21:07



volatile solo garantiza que todos los hilos, incluso ellos mismos, se incrementan. Por ejemplo: un contador ve la misma cara de la variable al mismo tiempo. No se utiliza en lugar de cosas sincronizadas, atómicas o de otro tipo, hace que las lecturas se sincronicen por completo. Por favor, no lo compare con otras palabras clave de Java. Como muestra el ejemplo a continuación, las operaciones de variables volátiles también son atómicas y fallan o tienen éxito a la vez.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

Incluso si pones volátil o no, los resultados siempre serán diferentes. Pero si usa AtomicInteger como a continuación los resultados serán siempre los mismos. Esto es lo mismo con sincronizado también.

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

5
2018-03-04 18:24