Notas del modelo de memoria JAVA (JMM)

Modelo de memoria JAVA

Las notas hechas aquí se combinan con el modelo de memoria Java en la JVM

Modelo de memoria Java en el arte de la programación concurrente con Java

Combina algunas preguntas de la entrevista.

Existe una clara diferencia entre el área de memoria JVM y el modelo de memoria JAVA

Distinguir la relación entre ellos.

JMM es una regla, principalmente para estudiar la visibilidad de la memoria multiproceso concurrente

Es una abstracción de proceso para que el caché lea y escriba el acceso

El propósito es asegurar que la memoria seaMemoria consistenteDe

Inserte la descripción de la imagen aquí

Memoria principal y memoria de trabajo.

El modelo de memoria Java crea una memoria de trabajo para cada hilo

Pero todas las variables deben almacenarse en la memoria principal, que no es la memoria del hardware.

Pero parte de la JVM
Inserte la descripción de la imagen aquí

Semántica de la memoria volátil

Semántica 1 Las modificaciones a las variables volátiles son inmediatamente visibles para otros hilos

Pero cálculos usando variables volátilesNoA prueba de hilos

Por ejemplo, si el subprocesamiento múltiple incrementa una variable volátil sin bloquearla, se produce un error de sincronización

Porque

Num ++ en realidad está compuesto por 4 operaciones de bytecode. Es probable que el número en la parte superior de la pila sea incrementado por otros hilos.

Resolución de problemas:

1. Puede usar la operación de incremento AtomicInteger es el método incrementAndGet ()

2. 方法 sincronizado

Escenario de uso:

1. El resultado de la operación no depende del valor actual, o los cambios de un solo hilo como el método set get no dependen del valor original

2. Las variables no requieren otros hilos para participar en restricciones variables

Semantic 2 Volatile prohíbe la optimización de reordenamiento de instrucciones

volatitle boolean  config = false ;

finishconfig(config);
   
config = true;

while(!config){
    sleep()}

dosomethings()

Si config no es una variable volátil

config puede ser más rápido que fhinishconfig

Esto puede hacer que otros se ejecuten antes de la configuración.

Resumir las tres características de las variables volátiles.

La lectura y la escritura en sí son atómicas, las otras no se incrementanAtomicidad

El funcionamiento de la variable volátil es visible para todos los subprocesos, y el caché se ve obligado a actualizar Visibilidad

Las variables volátiles no se pueden reordenar para agregar barreras de memoria Ordenada

Semántica de memoria de bloqueo

La liberación y adquisición de bloqueos es esencialmente una notificación de mensaje

Cuando un hilo libera un bloqueo, le dice al siguiente que adquiera el mensaje de bloqueo del hilo.

Cuando un hilo adquiere un bloqueo, en realidad recibe un mensaje de bloqueo de un hilo.

sincronizado es esencialmente similar al método de bloqueo y bloqueo

Las operaciones en sincronizado son atómicas porque solo se ejecuta un hiloAtomicidad

Después de que la operación sincronizada sea visible para todos los hilos, al desbloquear una variable se sincronizará la variable con la memoria principalVisibilidad

El código en sincronizado no se puede reordenar porque solo se está ejecutando un subproceso Ordenada

La implementación de algunos bloqueos es esencialmente para modificar la variable volátil para la operación CAS para lograr consistencia de memoria

Semántica de memoria de campos finales.

final es diferente de bloqueo y volátil

El acceso final al campo es similar a las variables ordinarias.

Pero el campo final debe obedecer dos reglas de reordenamiento

  1. La escritura del campo final en el constructor y la referencia posterior de este objeto se asignan a una variable de referencia. Esta operación no se puede reordenar
  2. Al leer una referencia a un campo final por primera vez, y posteriormente leer este campo final, esta operación no se puede reordenar
// 一个对象
static Ex object;
// 构造函数
public Ex(){
    // i 是 final域
    i=0;
    // j 是普通域
    j=2;
}
// A线程写
public void write(){
    object = new Ex()}
// B 线程读
public void read(){
    Ex obj  = object ;
    int a = obj.i;
    int b = obj.j;
}

Mira el ejemplo de arriba

El hilo B debe leer obj.i, el campo final, después de la inicialización del constructor.

Para obj.j, que es un campo común, el valor inicial no se lee necesariamente, pero puede ser el valor inicial predeterminado.

El campo final garantiza que el valor o referencia del objeto debe inicializarse correctamente

El campo final garantiza que la referencia del objeto debe leerse antes de leer el campo final del objeto.

sucede antes

La regla antecedente es un principio importante para juzgar si los datos compiten y si el hilo es seguro.

  1. La secuencia del programa se ejecuta en flujo de secuencia dentro de un solo hilo
  2. El bloqueo del tubo (bloqueo del monitor) desbloquea un bloqueo antes de que se bloquee
  3. El principio volátil escribe la variable volátil antes de leer la variable
  4. Principio de inicio de subproceso El método de inicio de subproceso start () se ejecuta antes de todas las operaciones en el subproceso
  5. Principio de terminación de subproceso Todas las operaciones del subproceso se ejecutan antes de la finalización del subproceso, como el método de finalización Thread.join
  6. El principio de la interrupción del hilo es que la operación interrumpida () del hilo precede a la detección de eventos de interrupción en el hilo interrumpido
  7. Principio de finalización de objeto El método de construcción de un objeto precede a su método de finalización
  8. Transitividad, A es anterior a B, B es anterior a C, entonces A es anterior a C

Doble control de bloqueo DCL

El bloqueo de doble verificación denominado DCL es una tecnología de carga diferida muy común

Es decir, para algunos objetos con una gran sobrecarga, se cargan cuando se usan. Carga bajo demanda. Carga perezosa

El siguiente es un objeto de inicialización no seguro para subprocesos

public class LazyInital {
    class Instatnce{
        
    }
    private static Instatnce instatnce;
    
    public Instatnce getInstatnce(){
        if(instatnce==null)      // Thread A
            instatnce = new Instatnce();  //  Thread B
        return instatnce;
    }
}

Un cambio simple es agregar el logotipo sincronizado al método getInstance.

Pero este rendimiento no es bueno.

A continuación se muestra la versión incorrecta de DCL

public Instatnce getInstatnce(){
    if(instatnce==null){
        synchronized (LazyInital.class){
            if(instatnce==null)
                instatnce = new Instatnce();
        }
    }
    return instatnce;
}

Esto se llama bloqueo de doble verificación y se ve muy cómodo pero tiene grandes problemas

La inicialización del objeto java se divide en 3 partes.

  1. memoria = asignar () // asignar memoria
  2. initclass (memoria) // inicializar objeto
  3. instancia = memoria // punto a objeto

2 y 3 pueden ser reordenados.

La última situación puede ser

El subproceso A no ha inicializado la instancia. El subproceso B ya ha obtenido una referencia al objeto. En este momento, devuelve un objeto no inicializado.

Solución

  1. No permitir la inicialización de objetos y reordenar objetos puntiagudos

Use la semántica de Volatile para
agregar la descripción de Volatile

  1. La inicialización de los objetos del hilo A puede reordenarse, pero no debe ser visible para el hilo B.

Utilice la solución de inicialización de clase, confíe en las características del bloqueo de objeto de clase de inicialización de JVM

public class InstanceFactory {
    static class Instance{

    }
    // instanceHolder类的初始化加锁
    private static class InstanceHolder{
        private static Instance instance = new Instance();
    }
    public static Instance getInstance(){
        return InstanceHolder.instance;
    }
}

La diferencia entre volátil y sincronizado

  1. La esencia de lo volátil es decirle a jvm que el valor de la variable actual en la memoria de trabajo es incierto, y debe leerse desde la memoria principal

    Los sincronizados bloquean la variable actual, que solo puede leerse un subproceso, y otros subprocesos se bloquean hasta que se libera el bloqueo.

  2. Nivel de variable volátil, variable de método sincronizado

  3. Volátil solo puede lograr la visibilidad de modificaciones variables, y no puede garantizar la atomicidad de las operaciones. Por ejemplo, la lectura y la escritura de Volatile son atómicas, pero las operaciones de Volatile no son necesariamente atómicas y se incrementan por sí mismas.

  4. Volátil no bloqueará, sincronizado bloqueará

  5. la variable volatitle no se optimizará, la sincronización se optimizará

22 artículos originales publicados · Me gusta2 · Visitas 881

Supongo que te gusta

Origin blog.csdn.net/weixin_41685373/article/details/105085104
Recomendado
Clasificación