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

En el trabajo de hoy, me encontré con la palabra clave volatile 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?

volatile tiene semántica para la visibilidad de la memoria. Básicamente, el valor de un campo volatile 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 una variable volatile para controlar si algún código continúa un ciclo. El ciclo prueba el valor volatile y continúa si es true . La condición puede establecerse en false llamando a un método de “detención”. El ciclo ve false y termina cuando prueba el valor después de que el método de detención completa la ejecución.

El libro ” Concurrencia de Java en la práctica “, que recomiendo mucho, ofrece una buena explicación de la 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 la volatile es lo que su artículo llama la “bandera de estado del patrón 1”.

Si desea obtener más información acerca de cómo funciona la volatile bajo el capó, lea sobre el modelo de memoria de Java . Si desea ir más allá de ese nivel, consulte un buen libro de architecture de computadoras como Hennessy & Patterson y lea sobre la coherencia del caché y la coherencia del caché.

“… el modificador volátil garantiza que cualquier hilo que lea un campo verá el valor escrito más reciente.” – Josh Bloch

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

La publicación de Wikipedia en un patrón Singleton muestra volatilidad en uso.

Punto importante sobre volatile :

  1. La sincronización en Java es posible utilizando palabras clave de Java synchronized y volatile y bloqueadas.
  2. En Java, no podemos tener variable synchronized . Usar palabras clave synchronized con una variable es ilegal y dará como resultado un error de comstackción. En lugar de utilizar la variable synchronized en Java, puede usar la variable volatile java, que instruirá a los subprocesos de JVM para que lean el valor de la variable volatile de la memoria principal y no lo almacenen en caché localmente.
  3. Si una variable no se comparte entre varios subprocesos, entonces no es necesario usar la palabra clave volatile .

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 volatile variable de _instance , entonces el subproceso que está creando la instancia de Singleton no puede comunicarse con el otro subproceso. Entonces, si el Subproceso A está creando una instancia de Singleton y justo después de la creación, la CPU corrompe, etc., todos los demás subprocesos no podrán ver el valor de _instance como 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 : la palabra clave volatile 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 comstackdor 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

uso volatile en Java :

Los iteradores a prueba de fallas generalmente se implementan utilizando un contador volatile en el objeto de la lista.

  • Cuando la lista se actualiza, el contador se incrementa.
  • Cuando se crea un Iterator , el valor actual del contador está incrustado en el objeto Iterator .
  • Cuando se realiza una operación Iterator , el método compara los dos valores del contador y arroja una 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

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

Un ejemplo común para usar volatile es usar una variable volatile boolean como indicador 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 que el marcador sea volatile , puede asegurarse de que el hilo que lo está revisando verá que se ha configurado la próxima vez que lo comprueba sin tener que usar un bloque synchronized .

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

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

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

  1. Mecanismo de locking comprobado . Usado a menudo en el patrón de diseño Singleton. 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 .

Una variable declarada con palabra clave volatile , tiene dos cualidades principales que lo hacen especial.

  1. Si tenemos una variable volátil, no se puede almacenar en caché en la memoria caché de la computadora (microprocesador) con ningún hilo. El acceso siempre sucedió desde la memoria principal.

  2. Si hay una operación de escritura en una variable volátil, y de repente se solicita una operación de lectura , se garantiza que la operación de escritura finalizará antes de la operación de lectura .

Dos cualidades anteriores deducen eso

  • Todos los hilos que leen una variable volátil definitivamente leerán el último valor. Porque ningún valor en caché puede contaminarlo. Y también la solicitud de lectura se otorgará solo después de la finalización de la operación de escritura actual.

Y por otro lado,

  • Si seguimos investigando el n . ° 2 que he mencionado, podemos ver que la palabra clave volatile es una forma ideal de mantener una variable compartida que tiene ‘n’ cantidad de subprocesos de lectura y solo una cadena de escritura para acceder a ella. Una vez que agreguemos la palabra clave volatile , ya está hecho. Sin ningún otro sobrecarga de seguridad de hilo.

Por el contrario,

No podemos utilizar palabras clave volatile únicamente para satisfacer una variable compartida que tiene más de un hilo de escritura accediendo a ella .

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’.

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 list = new ArrayList(); 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 list = new ArrayList(); 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); } } } 

Sí, lo uso bastante, puede ser muy útil para código de subprocesos múltiples. El artículo que mencionaste es bueno. Aunque hay dos cosas importantes a tener en cuenta:

  1. Solo debe usar volátil si comprende completamente lo que hace y cómo se sincroniza. En muchas situaciones, volátil parece ser, en la superficie, una alternativa más sencilla y más eficaz a la sincronización, cuando a menudo una mejor comprensión de la volatilidad dejaría en claro que la única opción que funcionaría sería sincronizada.
  2. volátil no funciona realmente en muchas JVM antiguas, aunque sí lo hace. Recuerdo haber visto un documento que hacía referencia a los distintos niveles de soporte en diferentes JVM, pero desafortunadamente no puedo encontrarlo ahora. Definitivamente investigue si está utilizando Java pre 1.5 o si no tiene control sobre las JVM en las que se ejecutará su progtwig.

Absolutamente sí. (Y no solo en Java, sino también en C #). Hay momentos en los que necesita obtener o establecer un valor que garantice que es una operación atómica en su plataforma determinada, un int o boolean, por ejemplo, pero no requiere la sobrecarga del locking de hilo. La palabra clave volátil le permite asegurarse de que cuando lee el valor obtiene el valor actual y no un valor en caché que acaba de quedar obsoleto al escribir en otro hilo.

Cada subproceso que acceda a un campo volátil leerá su valor actual antes de continuar, en lugar de (potencialmente) usar un valor en caché.

Solo la variable miembro puede ser volátil o transitoria.

Hay dos usos diferentes de palabra clave volátil.

  1. Impide que JVM lea valores de registro (asum como caché), y fuerza que su valor sea leído de la memoria.
  2. Reduce el riesgo de errores de consistencia en la memoria.

Impide que JVM lea valores en el registro, y obliga a leer su valor desde la memoria.

Un indicador de ocupado se usa para evitar que un hilo continúe mientras el dispositivo está ocupado y el indicador no está protegido por un locking:

 while (busy) { /* do something else */ } 

El hilo de prueba continuará cuando otro hilo apaga el indicador de ocupado :

 busy = 0; 

Sin embargo, como se accede con frecuencia al ocupado en el hilo de prueba, la JVM puede optimizar la prueba colocando el valor de ocupado en un registro, luego prueba el contenido del registro sin leer el valor de ocupado en la memoria antes de cada prueba. El hilo de prueba nunca vería ocupado cambiar y el otro hilo solo cambiaría el valor de ocupado en la memoria, lo que provocaría un interlocking. Declarar el indicador de ocupado como volátil obliga a leer su valor antes de cada prueba.

Reduce el riesgo de errores de consistencia de la memoria.

El uso de variables volátiles reduce el riesgo de errores de coherencia de memoria , ya que cualquier escritura en una variable volátil establece una relación de “sucede antes” con las lecturas posteriores de esa misma variable. Esto significa que los cambios en una variable volátil siempre son visibles para otros hilos.

La técnica de lectura, escritura sin errores de coherencia de memoria se llama acción atómica .

Una acción atómica es aquella que efectivamente sucede de una vez. Una acción atómica no puede detenerse en el medio: sucede completamente o no sucede en absoluto. Ningún efecto secundario de una acción atómica es visible hasta que la acción se complete.

A continuación se detallan las acciones que puede especificar que son atómicas:

  • Las lecturas y escrituras son atómicas para las variables de referencia y para la mayoría de las variables primitivas (todos los tipos, excepto el largo y el doble).
  • Las lecturas y escrituras son atómicas para todas las variables declaradas volátiles (incluidas variables largas y dobles).

¡Aclamaciones!

Las variables volátiles son una sincronización ligera. Cuando la visibilidad de los últimos datos entre todos los hilos es un requisito y la atomicidad puede verse comprometida, en tales situaciones las variables volátiles deben ser preferidas. Leer en variables volátiles siempre devuelve la escritura más reciente hecha por cualquier hilo, ya que no están ni en caché en registros ni en cachés donde otros procesadores no pueden ver. Volátil está libre de locking. Uso volátil, cuando el escenario cumple con los criterios mencionados anteriormente.

Desde la página de documentación de Oracle , surge la necesidad de una variable volátil para solucionar los problemas de coherencia de la memoria:

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 pasar antes a las lecturas posteriores de esa misma variable.

Esto significa que los cambios en una variable volatile 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 volatile , sino también los efectos secundarios del código que provocó el cambio.

Como se explica en la respuesta de Peter Parker , en ausencia de modificador volatile , la stack de cada subproceso puede tener su propia copia de variable. Al hacer que la variable sea volatile , se han solucionado los problemas de consistencia de la memoria.

Eche un vistazo a la página del tutorial de jenkov para una mejor comprensión.

Eche un vistazo a la pregunta SE relacionada para obtener más detalles sobre volátiles y casos de uso para usar volátiles:

Diferencia entre volátil y sincronizado en Java

Un caso de uso práctico:

Tiene muchos hilos, que necesitan imprimir la hora actual en un formato particular, por ejemplo: java.text.SimpleDateFormat("HH-mm-ss") . Yon puede tener una clase, que convierte la hora actual en SimpleDateFormat y actualiza la variable por cada segundo. Todos los otros hilos pueden simplemente usar esta variable volátil para imprimir la hora actual en los archivos de registro.

La clave volátil cuando se usa con una variable, se asegurará de que los hilos que lean esta variable verán el mismo valor. Ahora bien, si tiene múltiples hilos leyendo y escribiendo en una variable, hacer que la variable sea volátil no será suficiente y los datos se corromperán. Los subprocesos de imagen han leído el mismo valor, pero cada uno ha realizado algunos cambios (digamos un contador incrementado), cuando se vuelve a escribir en la memoria, se infringe la integridad de los datos. Es por eso que es necesario sincronizar el varible (son posibles diferentes maneras)

Si los cambios se realizan por 1 hilo y los demás solo necesitan leer este valor, el volátil será adecuado.

Volátil lo sigue.

1> La lectura y escritura de variables volátiles por diferentes subprocesos siempre provienen de la memoria, no de la propia memoria caché o el registro de la CPU. Entonces, cada hilo siempre trata con el último valor. 2> Cuando 2 subprocesos diferentes funcionan con la misma instancia o variables estáticas en el montón, uno puede ver las acciones de otros como desordenadas. Ver el blog de jeremy manson sobre esto. Pero volátil ayuda aquí.

El código que se ejecuta completamente muestra cómo se pueden ejecutar varios subprocesos en un orden predefinido e imprime las salidas sin utilizar palabras clave sincronizadas.

 thread 0 prints 0 thread 1 prints 1 thread 2 prints 2 thread 3 prints 3 thread 0 prints 0 thread 1 prints 1 thread 2 prints 2 thread 3 prints 3 thread 0 prints 0 thread 1 prints 1 thread 2 prints 2 thread 3 prints 3 

Para lograr esto, podemos usar el siguiente código de ejecución completo.

 public class Solution { static volatile int counter = 0; static int print = 0; public static void main(String[] args) { // TODO Auto-generated method stub Thread[] ths = new Thread[4]; for (int i = 0; i < ths.length; i++) { ths[i] = new Thread(new MyRunnable(i, ths.length)); ths[i].start(); } } static class MyRunnable implements Runnable { final int thID; final int total; public MyRunnable(int id, int total) { thID = id; this.total = total; } @Override public void run() { // TODO Auto-generated method stub while (true) { if (thID == counter) { System.out.println("thread " + thID + " prints " + print); print++; if (print == total) print = 0; counter++; if (counter == total) counter = 0; } else { try { Thread.sleep(30); } catch (InterruptedException e) { // log it } } } } } } 

El siguiente enlace github tiene un archivo Léame, que proporciona una explicación adecuada. https://github.com/sankar4git/volatile_thread_ordering

Me gusta la explicación de jenkov

La palabra clave volátil de Java se utiliza para marcar una variable de Java como “estar almacenado en la memoria principal”. Más precisamente, eso significa que cada lectura de una variable volátil se leerá desde la memoria principal de la computadora, y no desde la memoria caché de la CPU, y que cada escritura a una variable volátil se escribirá en la memoria principal, y no solo en la memoria caché de la CPU .

En realidad, desde Java 5, la palabra clave volátil garantiza más que solo el hecho de que las variables volátiles se escriben y leen desde la memoria principal. Es la garantía de visibilidad extendida llamada llamada “sucede antes de la garantía”.

Consideraciones de rendimiento de volátiles

La lectura y escritura de variables volátiles hace que la variable sea leída o escrita en la memoria principal. Leer y escribir en la memoria principal es más costoso que acceder a la memoria caché de la CPU. El acceso a las variables volátiles también evita el reordenamiento de la instrucción, que es una técnica normal de mejora del rendimiento. Por lo tanto, solo debe usar variables volátiles cuando realmente necesite forzar la visibilidad de las variables.

Una variable volátil se modifica de forma asíncrona mediante la ejecución simultánea de subprocesos en una aplicación Java. No está permitido tener una copia local de una variable que es diferente del valor que se encuentra actualmente 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. Por supuesto, es probable que las variables volátiles tengan una mayor sobrecarga de acceso y actualización que las variables “simples”, ya que los hilos de razón pueden tener su propia copia de datos para una mejor eficacia.

Cuando un campo se declara volátil, el comstackdor y el tiempo de ejecución reciben aviso de que esta variable se comparte y que las operaciones en ella no se deben reordenar con otras operaciones de memoria. Las variables volátiles no se almacenan en caché en registros o en cachés donde están ocultas de otras procesadores, por lo que una lectura de una variable volátil siempre devuelve la escritura más reciente por cualquier hilo.

como referencia, remítase a este http://techno-terminal.blogspot.in/2015/11/what-are-volatile-variables.html