Uso detallado de la palabra clave volátil en Java

Para resolver los problemas de atomicidad, visibilidad y orden en la programación concurrente, el lenguaje Java proporciona una serie de palabras clave relacionadas con el procesamiento concurrente, como , , , synchronizedetc. En el artículo [anterior][2], también presentamos el uso y el principio de . En este artículo, analicemos otra palabra clave: .volatilefinalconcurren包synchronizedvolatile

Este artículo gira en torno volatilea él, introduciendo principalmente volatileel uso, volatilelos principios y volatilecómo proporcionar visibilidad y garantías de orden.

volatileEsta palabra clave existe no solo en el lenguaje Java, sino también en muchos lenguajes, y su uso y semántica también son diferentes. Especialmente en lenguaje C, C++ y Java, existen volatilepalabras clave. Ambos pueden usarse para declarar variables u objetos. La siguiente es una breve introducción a las palabras clave en el lenguaje Java volatile.

uso de volátiles

volatilePor lo general, se compara con "ligero synchronized", y también es una palabra clave más importante en la programación concurrente de Java. Y synchronizeddiferente, volatilees un modificador de variable, solo se puede usar para modificar variables. Los métodos y bloques de código no se pueden modificar.

volatilevolatileEl uso de es relativamente simple, solo necesita usar la modificación al declarar una variable a la que pueden acceder varios subprocesos al mismo tiempo .

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

El código anterior es una implementación típica de un singleton en forma de verificación de doble bloqueo, en la que volatilese utilizan palabras clave para modificar el singleton al que pueden acceder varios subprocesos al mismo tiempo.

El principio de la volatilidad

En [Si alguien te pregunta cuál es el modelo de memoria de Java, envíale este artículo][1], hemos introducido que para mejorar la velocidad de ejecución del procesador, se añade una caché multinivel entre el procesador y la memoria. .para mejorar. Sin embargo, debido a la introducción de caché multinivel, existe un problema de inconsistencia de datos de caché.

Sin embargo, para volatilelas variables, cuando volatilese escribe la variable, la JVM enviará una instrucción de prefijo de bloqueo al procesador para escribir la variable en el caché de vuelta a la memoria principal del sistema.

Pero incluso si se vuelve a escribir en la memoria, si los valores almacenados en caché por otros procesadores aún son antiguos, habrá problemas al realizar las operaciones de cálculo. Por lo tanto, en multiprocesadores, para garantizar que los cachés de cada procesador son consistentes, se implementará.缓存一致性协议

Protocolo de consistencia de caché : cada procesador verifica si el valor de su caché ha expirado olfateando los datos propagados en el bus. Cuando el procesador encuentra que la dirección de memoria correspondiente a su propia línea de caché ha sido modificada, actualizará el caché del procesador actual. la línea está configurada en un estado no válido. Cuando el procesador quiera modificar los datos, se verá obligado a leer los datos de la memoria del sistema en el caché del procesador nuevamente.

Por lo tanto, si se volatilemodifica una variable, su valor se verá forzado a vaciarse en la memoria principal después de cada cambio de datos. Las cachés de otros procesadores también cargarán el valor de esta variable desde la memoria principal en sus propias cachés porque cumplen con el protocolo de coherencia de caché. Esto asegura que volatileun valor sea visible en múltiples cachés en programación concurrente.

volátil y visibilidad

La visibilidad significa que cuando varios subprocesos acceden a la misma variable, un subproceso modifica el valor de la variable y otros subprocesos pueden ver inmediatamente el valor modificado.

Lo analizamos en [Si alguien te pregunta cuál es el modelo de memoria de Java, envíale este artículo][1]: El modelo de memoria de Java estipula que todas las variables se almacenan en la memoria principal, y cada hilo tiene su propia memoria de trabajo. el subproceso almacena la copia de la memoria principal de las variables utilizadas en el subproceso en la memoria de trabajo del subproceso.Todas las operaciones sobre las variables por parte del subproceso deben realizarse en la memoria de trabajo, y la memoria principal no se puede leer ni escribir directamente. Diferentes subprocesos no pueden acceder directamente a las variables en la memoria de trabajo de cada uno, y la transferencia de variables entre subprocesos requiere la sincronización de datos entre su propia memoria de trabajo y la memoria principal. Por lo tanto, puede suceder que el hilo 1 cambie el valor de una variable, pero el hilo 2 no sea visible.

Como se mencionó anteriormente volatile, las palabras clave en Java volatileproporcionan una función, es decir, las variables modificadas por ellas se pueden sincronizar con la memoria principal inmediatamente después de ser modificadas, y las variables modificadas por ellas se restablecen desde la memoria principal antes de cada uso. está enrojecido. Por lo tanto, se puede utilizar volatilepara garantizar la visibilidad de las variables durante las operaciones de subprocesos múltiples.

volátil y ordenado

Secuencia significa que el orden de ejecución del programa se ejecuta en el orden del código.

Lo analizamos en [Si alguien le pregunta cuál es el modelo de memoria Java, envíele este artículo][1]: Además de la introducción de intervalos de tiempo, debido a la optimización del procesador y la reorganización de instrucciones, la CPU también puede ingresar código para Ejecución de orden, por ejemplo, load->add->savepuede optimizarse en load->save->add. Aquí es donde puede haber un problema de orden.

Además volatilede garantizar la visibilidad de los datos, también tiene una función poderosa, es decir, puede prohibir la optimización del reordenamiento de instrucciones, etc.

Las variables ordinarias solo garantizan que se puedan obtener los resultados correctos en los lugares de los que dependen los resultados de la asignación durante la ejecución del método, pero no pueden garantizar que el orden de las operaciones de asignación de variables sea consistente con el orden de ejecución en el código del programa.

Volatile puede prohibir la reorganización de las instrucciones, lo que garantiza que el programa del código se ejecutará estrictamente de acuerdo con el orden del código. Esto asegura el orden. La operación de volatilela variable modificada se ejecutará estrictamente de acuerdo con la secuencia del código, y load->add->savela secuencia de ejecución es: cargar, agregar, guardar.

volatilidad y atomicidad

La atomicidad significa que una operación es ininterrumpida y debe ejecutarse por completo, o no se ejecutará en absoluto.

¿Qué está pasando con nuestro problema de subprocesos múltiples en [la programación concurrente de Java? ][3] analizado: el subproceso es la unidad básica de programación de la CPU. La CPU tiene el concepto de intervalos de tiempo y realizará la programación de subprocesos de acuerdo con diferentes algoritmos de programación. Cuando un subproceso comienza a ejecutarse después de obtener el intervalo de tiempo, después de que se agote el intervalo de tiempo, perderá el derecho a usar la CPU. Por lo tanto, en un escenario de subprocesos múltiples, dado que los intervalos de tiempo se rotan entre subprocesos, se producirán problemas de atomicidad.

En el artículo anterior, synchronizedcuando lo presentamos, mencionamos que para garantizar la atomicidad, necesitamos pasar instrucciones de bytecode monitorentery monitorexit, pero volatileno hay relación entre estas dos instrucciones.

Por lo tanto, volatileno se puede garantizar la atomicidad.

volatileSe puede utilizar en su lugar en los siguientes dos escenarios synchronized:

1. El resultado de la operación no depende del valor actual de la variable, o puede garantizar que solo un único hilo modificará el valor de la variable.

2. Las variables no necesitan participar en restricciones invariantes con otras variables de estado.

Además de los escenarios anteriores, se deben usar otros métodos para garantizar la atomicidad, como synchronizedo concurrent包.

Veamos un ejemplo de volátil y atomicidad:

public class Test {
    public volatile int inc = 0;

    public void increase() {
        inc++;
    }

    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }

        while(Thread.activeCount()>1)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println(test.inc);
    }
}

El código anterior es relativamente simple, es decir, crea 10 subprocesos y luego realiza 1000 i++operaciones respectivamente. En circunstancias normales, la salida del programa debería ser 10000, pero los resultados de varias ejecuciones son todos inferiores a 10000. Esta es en realidad volatilela razón por la que no se puede satisfacer la atomicidad.

¿Por qué sucede esto? Es porque aunque volátil puede garantizar incla visibilidad entre múltiples subprocesos. Pero inc++la atomicidad no puede.

Resumen y reflexión

Cubrimos volatilepalabras clave y synchronizedpalabras clave. Ahora sabemos que synchronizedse puede garantizar la atomicidad, el orden y la visibilidad. Pero volatilesolo se puede garantizar el orden y la visibilidad.

Entonces, echemos un vistazo al singleton implementado por el bloqueo de doble verificación. Ya se ha utilizado synchronized, ¿por qué todavía se necesita volatile?

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

Supongo que te gusta

Origin blog.csdn.net/zy_dreamer/article/details/132350560
Recomendado
Clasificación