jcolorchooser - usar codigo html en java




¿Alguna vez usas la palabra clave volátil en Java? (14)

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

Teoría y práctica de Java: Gestionando la volatilidad.

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


Absolutamente sí. (Y no solo en Java, sino también en C #). Hay ocasiones en las que necesita obtener o establecer un valor que garantice ser una operación atómica en su plataforma determinada, por ejemplo, int o boolean, pero no requiere La sobrecarga de bloqueo de hilo. La palabra clave volátil le permite asegurarse de que cuando lea el valor obtenga el valor actual y no un valor almacenado en caché que se haya quedado obsoleto por una escritura en otro subproceso.


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

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


En mi opinión, dos escenarios importantes distintos a la detención del subproceso en el que se utiliza la palabra clave volátil son

  1. Mecanismo de bloqueo de doble control . Se utiliza a menudo en el patrón de diseño Singleton. En este caso, el singleton object needs to be declared volatile .
  2. Despertadores espurios . El hilo a veces puede despertarse de una llamada en espera incluso si no se ha emitido una llamada de notificación. Este comportamiento se llama activación supurativa. Esto se puede contrarrestar usando una variable condicional (indicador booleano). Coloque la llamada en espera () en un bucle while siempre que la marca sea verdadera. Por lo tanto, si el subproceso se despierta de una llamada en espera debido a cualquier otro motivo que no sea notificar / notificar, entonces se encuentra el indicador que sigue siendo verdadero y, por lo tanto, las llamadas vuelven a esperar. Antes de llamar a notificar, establezca este indicador en verdadero. En este caso, la boolean flag is declared as volatile .

Hay dos usos diferentes de la palabra clave volátil.

  1. Evita que la JVM lea los valores del registro (se asume como caché) y obliga a que su valor se lea de la memoria.
  2. Reduce el riesgo de errores de falta de coherencia de memoria.

Evita que la JVM lea los valores en el registro y obliga a que su valor se lea de la memoria.

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

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

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

busy = 0;

Sin embargo, dado que se accede a busy en el subproceso de prueba, la JVM puede optimizar la prueba al colocar el valor de busy en un registro, luego probar el contenido del registro sin leer el valor de busy en la memoria antes de cada prueba. El subproceso de prueba nunca vería un cambio de ocupado y el otro subproceso solo cambiaría el valor de ocupado en la memoria, lo que provocaría un punto muerto. 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 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 "sucede antes de" con lecturas subsiguientes de esa misma variable. Esto significa que los cambios en una variable volátil siempre son visibles para otros subprocesos.

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

Una acción atómica es aquella que efectivamente sucede todo al mismo tiempo. Una acción atómica no puede detenerse en el medio: o bien 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 completa.

A continuación se muestran 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 largos y dobles).
  • Las lecturas y escrituras son atómicas para todas las variables declaradas volátiles (incluidas las variables largas y dobles).

¡Aclamaciones!


Las variables volátiles son la sincronización ligera. Cuando la visibilidad de los últimos datos entre todos los subprocesos es un requisito y la atomicidad puede verse comprometida, en tales situaciones deben preferirse las variables volátiles. Leer sobre variables volátiles siempre devuelve la escritura más reciente realizada por cualquier hilo, ya que no se almacenan en caché en registros ni en cachés donde otros procesadores no pueden ver. Volatile is Lock-Free. Yo uso volátil, cuando el escenario cumple con los criterios mencionados anteriormente.


Lo volátil lo sigue.

1> La lectura y escritura de variables volátiles por diferentes subprocesos provienen siempre de la memoria, no de la caché del propio subproceso o del registro de la CPU. Así que 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 fuera de orden. Ver el blog de jeremy manson sobre esto. Pero las ayudas volátiles aquí.

El siguiente código en ejecución muestra cómo una serie de subprocesos pueden ejecutarse en un orden predefinido e imprimir salidas sin usar 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 utilizar 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 de github tiene un archivo Léame, que proporciona una explicación adecuada. https://github.com/sankar4git/volatile_thread_ordering


Nadie ha mencionado el tratamiento 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. @link


Sí, lo uso bastante; puede ser muy útil para el 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 volatile si entiende completamente lo que hace y cómo difiere en la sincronización. En muchas situaciones, volátil aparece, en la superficie, como una alternativa más simple y más eficiente 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 la sincronización.
  2. volatile en realidad no funciona en muchas JVM antiguas, aunque sí lo hace la sincronización. Recuerdo haber visto un documento que hacía referencia a los diversos niveles de soporte en diferentes JVM, pero desafortunadamente no lo puedo encontrar ahora. Definitivamente, investigue si está utilizando Java pre 1.5 o si no tiene control sobre las JVM en las que se ejecutará su programa.

Tendrá que usar palabras clave 'volátiles' o 'sincronizadas' y cualquier otra herramienta y técnica de control de concurrencia que pueda tener a su disposición si está desarrollando una aplicación de multiproceso. Ejemplo de tal aplicación es 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 el control de concurrencia como ya lo abordó el servidor de aplicaciones. De hecho, si recordaba correctamente, el estándar Java EE prohíbe cualquier control de concurrencia en servlets y EJBs, ya que es parte de la capa de 'infraestructura' que se supone que debe liberarse de su manejo. Solo hace el control de concurrencia en dicha aplicación si está implementando objetos singleton. Esto incluso se aborda si teje tus componentes utilizando 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 tendría que usar "volátil".


Un ejemplo común para usar volatile es usar una variable volatile boolean como una bandera para terminar un hilo. Si ha iniciado un hilo y desea poder interrumpirlo con seguridad desde un hilo diferente, puede hacer que el hilo verifique periódicamente una bandera. Para detenerlo, establece la bandera en verdadero. Al hacer que el indicador sea volatile , puede asegurarse de que el hilo que está verificando verá que se configuró la próxima vez que lo verifique sin tener que usar un bloque synchronized .


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

  1. Si tenemos una variable volátil, no se puede almacenar en la memoria caché de la computadora (microprocesador) por 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 que

  • Todos los hilos que leen una variable volátil definitivamente leerán el último valor. Porque ningún valor almacenado 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 investigamos más a fondo el # 2 que he mencionado, podemos ver que la palabra clave volatile es una forma ideal de mantener una variable compartida que tiene 'n' número de subprocesos de lectura y solo un subproceso de escritura para acceder a ella. Una vez que agregamos la palabra clave volatile , se hace. No hay ninguna otra sobrecarga sobre la seguridad del hilo.

A la inversa

No podemos hacer uso exclusivo de palabras clave volatile , para satisfacer una variable compartida que tiene más de un hilo de escritura accediendo a ella .


volátil es muy útil para detener hilos.

No es que debas escribir tus propios subprocesos, Java 1.6 tiene muchos conjuntos de subprocesos agradables. Pero si está seguro de que necesita un hilo, deberá saber cómo detenerlo.

El patrón que uso para los 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 que no hay necesidad de sincronización


volatile tiene semántica para la visibilidad de la memoria. Básicamente, el valor de un campo volatile vuelve visible para todos los lectores (en particular, otros hilos) después de que se complete una operación de escritura 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 un código continúa un ciclo. El bucle prueba el valor volatile y continúa si es true . La condición se puede establecer en false llamando a un método de "parada". El bucle ve false y termina cuando prueba el valor después de que el método de detención completa la ejecución.

El libro " Java Concurrency in Practice ", que recomiendo ampliamente, brinda 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 al final de ese artículo). Mi uso de volatile es lo que su artículo llama "indicador de estado del patrón 1".

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


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

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

La publicación de Wikipedia en un patrón Singleton muestra volátil en uso.





volatile