Palabras clave volátiles de Java
En el procesamiento de concurrencia de hilo Java, hay mucha confusión en el uso de una palabra clave volátil. Creo que usar esta palabra clave puede hacer que todo vaya bien cuando se usa el procesamiento de concurrencias múltiples.
El lenguaje Java admite múltiples subprocesos. Para resolver el problema de la concurrencia de hilo, el bloque sincrónico y el mecanismo de palabras clave volátil se introducen dentro del lenguaje.
sincronizado
Todos están familiarizados con los bloques sincronizados, y se implementan a través de la palabra clave sincronizada. Con declaraciones sincronizadas y de bloque, solo un hilo puede usarlos al mismo tiempo al acceder a múltiples hilos.
Método o bloque de código modificado sincronizado.
volátil
Para las variables modificadas con volátiles, el hilo leerá el valor más modificado de la variable cada vez que usa la variable. Volátil se usa fácilmente y se usa para operaciones atómicas.
Veamos un ejemplo a continuación. Implementamos un contador. Cada vez que se inicia el hilo, se llamará al método contador Inc para agregar uno al mostrador.
Entorno de ejecución - Versión JDK: JDK1.6.0_31, memoria: 3G CPU: x86 2.4g
Contador de clase pública {public static int count = 0; public static void Inc () {// El retraso aquí es 1 milisegundo, lo que hace que el resultado sea obvio try {Thread.sleep (1); } capt (interruptedException e) {} count ++; } public static void main (string [] args) {// Inicie 1000 hilos al mismo tiempo para realizar cálculos I ++ y vea el resultado real para (int i = 0; i <1000; i ++) {new Thread (new Runnable () {@Override public void run () {contador.inc ();}}). Start (); } // El valor de cada ejecución aquí puede ser diferente, tal vez 1000 System.out.println ("Ejecutar resultado: contador.count =" + contador.count); }}运行结果:Counter.count= 995
实际运算结果每次可能都不一样,本机的结果为:运行结果:Counter.count= 995 ,可以看出,在多线程的环境下,Counter.count并没有期望结果是1000
Muchas personas piensan que este es un problema de concurrencia multiproceso. Solo necesita agregar volatile很多人以为,这个是多线程并发问题,只需要在变量count之前加上就可以避免这个问题,那我们在修改代码看看,看看结果是不是符合我们的期望
Contador de clase pública {public volatile static int count = 0; public static void Inc () {// El retraso aquí es 1 milisegundo, lo que hace que el resultado sea obvio try {Thread.sleep (1); } capt (interruptedException e) {} count ++; } public static void main (string [] args) {// Inicie 1000 hilos al mismo tiempo, realice cálculos I ++ y vea el resultado real para (int i = 0; i <1000; i ++) {new Thread (new Runnable () {@Override public void run () {contunder.inc ();}). Start (); } // El valor de cada ejecución aquí puede ser diferente, posiblemente 1000 System.out.println ("Ejecutar resultado: contador.count =" + contador.count); }}Resultado en ejecución: contador.count = 992
El resultado de la operación aún no es tan 1000 como esperábamos. Analicemos las razones a continuación
En el artículo de Java Garbage Collection, se describe la asignación de la memoria en el momento de JVM. Una de las áreas de memoria es la pila de máquina virtual JVM, y cada hilo tiene una pila de subprocesos cuando se ejecuta.
La pila de subprocesos guarda la información del valor variable durante el tiempo de ejecución del hilo. Cuando un subproceso accede al valor de un cierto objeto, primero encuentre el valor de la variable correspondiente a la memoria del montón a través de la referencia del objeto, y luego coloque la memoria del montón
El valor específico de la variable se carga en la memoria local del hilo y se crea una copia de la variable. Después de eso, el subproceso ya no tiene ninguna relación con el valor variable del objeto en la memoria del montón, sino que modifique directamente el valor de la variable de copia.
En un cierto momento después de la modificación (antes de que salga el subproceso), el valor de la copia de la variable de subproceso se vuelve a escribir automáticamente a la variable del objeto en el montón. De esta manera, el valor del objeto en el montón cambiará. La siguiente imagen
Describe esta interacción de escritura
Leer y cargar variables de copia desde la memoria principal a la memoria de trabajo actual
Use y asigne el código de ejecución para cambiar el valor de la variable compartida
Almacene y escriba Actualizar contenido relacionado con la memoria principal con datos de memoria de trabajo
donde el uso y la asignación puede aparecer varias veces
Sin embargo, estas operaciones no son atómicas, es decir, después de la carga de lectura, si la variable de recuento de memoria principal se modifica, el valor en la memoria de trabajo de subproceso no causará cambios correspondientes desde que se ha cargado, por lo que el resultado calculado será diferente de la esperada.
Para las variables modificadas por Volatile, la máquina virtual JVM solo garantiza que el valor cargado de la memoria principal a la memoria de trabajo de subprocesos sea la última
Por ejemplo, si el hilo 1 y el hilo 2 realizan operaciones de lectura y carga, y encuentran que el valor del recuento en la memoria principal es 5, entonces el último valor se cargará
Después de que el recuento de montón se modifique en el subproceso 1, se escribirá en la memoria principal, y la variable de recuento en la memoria principal se convertirá en 6.
Dado que Thread 2 ya ha realizado la operación de lectura y carga, el valor variable del recuento de memoria principal también se actualizará a 6 después de la operación.
Esto hace que ocurra la concurrencia después de que dos hilos se modifican con la palabra clave volátil en el tiempo.
Gracias por leer, espero que pueda ayudarte. ¡Gracias por su apoyo para este sitio!