La serie JUC de entrevistas en Java: hable sobre la comprensión de Volatile

  • JUC (java.util.concurrent)
    • Procesos e hilos
      • Proceso: un programa que se ejecuta en segundo plano (un software que abrimos es un proceso)
      • Subproceso: un proceso ligero y un proceso contiene varios subprocesos (en el mismo software, ejecutar Windows al mismo tiempo es un subproceso)
    • Simultaneidad y paralelismo
      • Simultaneidad: el acceso a algo al mismo tiempo es simultaneidad
      • Paralelo: hacer algo juntos es paralelo
  • Tres paquetes bajo la JUC
    • java.util.concurrent
      • java.util.concurrent.atomic
      • java.util.concurrent.locks

Hablar sobre la comprensión de volátil

Volátil no es aplicable en el entorno diario de un solo subproceso

  • Volátil es 轻量级un mecanismo de sincronización (tres características) proporcionado por la máquina virtual Java
    • Asegurar la visibilidad
    • Sin garantía de atomicidad
    • Prohibir la reordenación de pedidos

Que es JMM

JMM es el modelo de memoria de Java, también conocido como modelo de memoria de Java, JMM para abreviar. Es un concepto abstracto en sí mismo y en realidad no existe. Describe un conjunto de reglas o especificaciones a través de las cuales se define cada variable en el programa (incluyendo campos de instancia, campos estáticos y elementos que componen el objeto de matriz) métodos de acceso

Normativa JMM sobre sincronización:

  • Antes de que se desbloquee el hilo, el valor de la variable compartida debe vaciarse de nuevo a la memoria principal
  • Antes de que se desbloquee el hilo, debe leer el último valor de la memoria principal en su propia memoria de trabajo
  • El bloqueo y el desbloqueo son el mismo bloqueo.

Dado que la entidad del programa en ejecución de la JVM es un subproceso, y cuando se crea cada subproceso, la JVM creará una memoria de trabajo (llamada espacio de pila en algunos lugares) para él. La memoria de trabajo es el área de datos privados de cada subproceso , y el modelo de memoria de Java estipula que todas las variables se almacenan en la memoria principal. La memoria principal es un área de memoria compartida, a la que pueden acceder todos los subprocesos. No se pueden 但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写会主内存manipular directamente las variables en la memoria principal. La memoria de trabajo de cada subproceso almacena una copia de la variable en la memoria principal, por lo que es diferente No se puede acceder a la memoria de trabajo de cada uno entre subprocesos. La comunicación (valor de paso) entre subprocesos debe completarse a través de la memoria principal. El proceso de acceso breve es como sigue:

imagen-20200309153225758

Tasa de transferencia de datos: disco duro <memoria << caché <CPU

Dos conceptos mencionados anteriormente: memoria principal y memoria de trabajo

  • Memoria principal: la memoria de la computadora, que a menudo se menciona memoria 8G, memoria 16G

  • Memoria de trabajo: pero creamos un estudiante nuevo, entonces la edad = 25 también se almacena en la memoria principal

    • Cuando tres subprocesos acceden a la variable edad en el alumno al mismo tiempo, cada subproceso copiará una copia a su propia memoria de trabajo, realizando así la copia de la variable

    imagen-20200309154435933

Es decir: la visibilidad del modelo de memoria JMM se refiere a que cuando un subproceso escribe y cambia el valor en el área de memoria principal, otros subprocesos conocerán inmediatamente el valor cambiado y volverán a obtener el valor modificado.

Coherencia de caché

¿Por qué otros subprocesos pueden saberlo inmediatamente después de que se cambia un cierto valor en el subproceso principal? De hecho, aquí se utiliza la tecnología de detección de autobuses.

Antes de hablar sobre la tecnología de rastreo, primero hablemos del problema de la coherencia de la caché, es decir, cuando las tareas de computación de múltiples procesadores involucran la misma área de memoria principal, puede causar que sus propios datos de caché sean inconsistentes.

Para resolver el problema de la consistencia de la caché, cada procesador debe seguir algunos protocolos al acceder a la caché y operar de acuerdo con el protocolo al leer y escribir. Dichos protocolos incluyen principalmente MSI, MESI, etc.

MESES

Cuando la CPU escribe datos, si se encuentra que la variable operativa es una variable compartida, es decir, existe una copia de la variable en otras CPU, enviará una señal a otras CPU para configurar la línea de caché de la variable de memoria para que sea inválido, por lo que cuando otras CPU leen Cuando se encuentra esta variable, se encuentra que la línea de caché que almacena la variable en caché no es válida, entonces se volverá a leer de la memoria.

Bus olfateando

Entonces, ¿cómo puede saber si los datos no son válidos?

Aquí está el uso de la tecnología de rastreo de bus, es decir, cada procesador verifica si su valor de caché ha expirado rastreando los datos extendidos en el bus. Cuando el procesador encuentra que la dirección de memoria correspondiente a su línea de caché ha sido modificada, La línea de caché del procesador actual se establece en un estado no válido.Cuando el procesador modifica estos datos, volverá a leer los datos de la memoria al caché del procesador.

Tormenta de autobuses

¿Cuáles son las desventajas de la tecnología de detección de autobuses?

Debido a que el protocolo de coherencia de caché MESI de Volatile requiere un rastreo constante de la memoria principal y los ciclos CAS, la interacción no válida hará que el ancho de banda del bus alcance su punto máximo. Por lo tanto, no use la palabra clave volátil en grandes cantidades. En cuanto a cuándo usar volátil, cuándo usar bloqueos y Syschonized, debe basarse en escenarios reales.

Características de JMM

Tres características de JMM, volátil solo garantiza dos, visibilidad y orden, y no satisface la atomicidad

  • Visibilidad
  • Atomicidad
  • Orden

Verificación del código de visibilidad

Pero cuando no agregamos ninguna modificación a la variable miembro, no podemos percibir el valor modificado de otros hilos

package com.moxi.interview.study.thread;

/**
 * Volatile Java虚拟机提供的轻量级同步机制
 *
 * 可见性(及时通知)
 * 不保证原子性
 * 禁止指令重排
 *
 */

import java.util.concurrent.TimeUnit;

/**
 * 假设是主物理内存
 */
class MyData {
    
    

    int number = 0;

    public void addTo60() {
    
    
        this.number = 60;
    }
}

/**
 * 验证volatile的可见性
 * 1. 假设int number = 0, number变量之前没有添加volatile关键字修饰
 */
public class VolatileDemo {
    
    

    public static void main(String args []) {
    
    

        // 资源类
        MyData myData = new MyData();

        // AAA线程 实现了Runnable接口的,lambda表达式
        new Thread(() -> {
    
    

            System.out.println(Thread.currentThread().getName() + "\t come in");

            // 线程睡眠3秒,假设在进行运算
            try {
    
    
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            // 修改number的值
            myData.addTo60();

            // 输出修改后的值
            System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);

        }, "AAA").start();

        while(myData.number == 0) {
    
    
            // main线程就一直在这里等待循环,直到number的值不等于零
        }

        // 按道理这个值是不可能打印出来的,因为主线程运行的时候,number的值为0,所以一直在循环
        // 如果能输出这句话,说明AAA线程在睡眠3秒后,更新的number的值,重新写入到主内存,并被main线程感知到了
        System.out.println(Thread.currentThread().getName() + "\t mission is over");

        /**
         * 最后输出结果:
         * AAA	 come in
         * AAA	 update number value:60
         * 最后线程没有停止,并行没有输出  mission is over 这句话,说明没有用volatile修饰的变量,是没有可见性
         */

    }
}

El resultado de salida es

imagen-20200309162154191

Finalmente, el hilo no se detuvo, y la misión de la oración finalizada no se emitió en paralelo, lo que indica que no hay una variable modificada con volátil y no hay visibilidad.

Cuando modificamos las variables miembro en la clase MyData y agregamos la modificación de la palabra clave volátil

/**
 * 假设是主物理内存
 */
class MyData {
    
    
    /**
     * volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知
     */
    volatile int number = 0;

    public void addTo60() {
    
    
        this.number = 60;
    }
}

El resultado final es:

imagen-20200309162314054

También se ha ejecutado el hilo principal, lo que indica que la variable modificada volátil tiene el mecanismo de sincronización ligero JVM y puede percibir el valor modificado de otros hilos.

Supongo que te gusta

Origin blog.csdn.net/weixin_43314519/article/details/110195157
Recomendado
Clasificación