Modelo de memoria Java de la serie JVM (JMM)

El modelo de memoria Java ** (modelo de memoria Java, JMM) ** es diferente del área de datos de tiempo de ejecución de JVM. Los dos son conceptos completamente diferentes y no deben confundirse.

Uno, la diferencia entre JMM y JVM

El área de datos en tiempo de ejecución de JVM es una división lógica de la memoria ocupada por el proceso de Java durante el tiempo de ejecución de la máquina virtual Java, que incluye el área de método, la memoria de pila, la pila de la máquina virtual, la pila de método local y el contador de programa. Estos bloques son en realidad diferentes usos de la memoria solicitada por el proceso Java a través de diferentes estructuras de datos bajo la operación de la máquina virtual Java.

El modelo de memoria Java es la especificación del lenguaje Java para leer y escribir variables compartidas (en realidad, las operaciones de memoria correspondientes a las variables compartidas) en el caso de concurrencia multiproceso. Se utiliza principalmente para proteger diferentes sistemas operativos y hardware diferente cuando Java programas de acceso a la memoria compartida. con el fin de resolver los problemas de visibilidad multi-hilo, la atomicidad y así sucesivamente .

Después de escribir el programa, el compilador y el procesador tendrán las optimizaciones correspondientes para mejorar la eficiencia operativa. Hay muchas optimizaciones, como el reordenamiento de instrucciones. Las instrucciones se reordenan y se mejora el rendimiento, pero ¿podemos seguir obteniendo el resultado de ejecución que queremos?

La premisa de la optimización es que los resultados de la ejecución sigan siendo correctos, lo que requiere garantías adicionales JMM garantiza a los programadores de Java que los resultados siguen siendo correctos después de la optimización y se mejora el rendimiento.

En segundo lugar, el principio de ocurre antes

¿Cómo se asegura JMM de que los resultados aprobados para mejorar el rendimiento sigan siendo correctos? La especificación JVM estipula algunas reglas para las operaciones de memoria multiproceso de la máquina virtual Java: el principio de sucede antes . Se refleja principalmente en las dos palabras clave volátiles y sincronizadas.

Los ocho principios de ocurre antes (el principio de ocurre antes no puede entenderse simplemente literalmente como una operación ocurre antes de otra operación ):

  • Principio de suceder antes de un solo hilo: en el mismo hilo, escriba las operaciones que siguen a suceder antes de las operaciones anteriores.
  • El principio de pasar antes de la cerradura: la operación de desbloqueo de la misma cerradura ocurre antes de la operación de cerradura de esta cerradura.
  • Principio de suceder antes de volátiles: la operación de escritura de una variable volátil ocurre antes de cualquier operación de esta variable (por supuesto, también incluye la operación de escritura).
  • El principio de transitividad de suceder-antes: si ocurre una operación -antes de la operación B, ocurre la operación B-antes de la operación C, entonces ocurre una operación-antes de la operación C.
  • El principio de pasar antes del inicio del hilo: el método de inicio del mismo hilo ocurre antes que otros métodos de este hilo.
  • El principio de pasar antes de la interrupción del hilo: la llamada al método de interrupción del hilo pasa antes es el código que detecta la interrupción enviada por el hilo interrumpido.
  • El principio de suceder antes de la terminación del hilo: todas las operaciones en el hilo ocurren antes de la detección de la terminación del hilo.
  • El principio de suceder antes de la creación de objetos: la inicialización de un objeto se completa antes de que se llame a su método de finalización.

En el mismo hilo, las operaciones escritas en la operación anterior ocurren antes: muchos artículos entienden esto como el código escrito en el frente y el código escrito en la parte posterior, pero el reordenamiento de las instrucciones puede hacer que el código escrito en la parte posterior regrese. primero. Se produce a partir del código escrito en el frente. Esta es la interpretación de suceder antes como "antes de lo que sucede" De hecho, suceder antes no tiene ningún significado temporal aquí. Por ejemplo, el siguiente código:

int a = 3;      //1
int b = a + 1; //2

Aquí // la operación de asignar un valor a b usará la variable a, entonces el "principio de ocurrencia de un solo hilo antes" de java garantiza que el valor de a en /// 2 debe ser 3, no 0 y otros valores, porque / / 1 se escribe delante de /// 2, // 1 operación de asignación a la variable a debe ser visible para /// 2. Debido a que la variable a en // se usa en /// 2, y el modelo de memoria de Java proporciona el "principio de un solo hilo antes de pasar", la máquina virtual de Java no permite que el sistema operativo instruya la operación de // // 2 Reordenación, es decir, es imposible que // suceda antes de /// 1. Pero para el siguiente código:

int a = 3;
int b = 4;

Las dos declaraciones no tienen dependencias directas, por lo que puede ocurrir un reordenamiento de instrucciones, es decir, la asignación a b puede preceder a la asignación a a.

La operación de desbloqueo de la misma cerradura ocurre antes de la operación de cerradura de esta cerradura: no digas mucho, solo mira el siguiente código:

public class A {
    
    
   public int var;

   private static A a = new A();

   private A(){
    
    }

   public static A getInstance(){
    
    
       return a;
   }

   public synchronized void method1(){
    
    
       var = 3;
   }

   public synchronized void method2(){
    
    
       int b = var;
   }

   public void method3(){
    
    
       synchronized(new A()){
    
     //注意这里和method1 method2 用的可不是同一个锁哦
           var = 4;
       }
   }
}
main(){
    
    
   	//线程1执行的代码:
	A.getInstance().method1(); 

	//线程2执行的代码:
	A.getInstance().method2(); 

	//线程3执行的代码:
	A.getInstance().method3(); 
    
}

Si el "subproceso 1" se ejecuta en un momento determinado, el "subproceso 2" se ejecutará inmediatamente, porque el "subproceso 1" debe liberar el bloqueo después de ejecutar el método method1 de la clase A, y el "subproceso 2" debe obtenerlo antes de ejecutar el método2 método de clase A El bloqueo se ajusta al "principio de bloqueo antes de pasar", entonces la variable var en el "hilo 2" método método2 debe ser 3, por lo que el valor de la variable b también debe ser 3. Pero si está en el orden de "Subproceso 1", "Subproceso 3" y "Subproceso 2", ¿el valor de b en el último "Subproceso 2" método 2 método 3 o 4? El resultado es 3 o 4. Es cierto que el "subproceso 3" tiene que ser desbloqueado después de ejecutar el método3, y luego el "subproceso 2" tiene un bloqueo, pero estos dos subprocesos no están usando el mismo bloqueo, por lo que las dos operaciones de JMM no cumplen con las ocho suceden -antes Por lo tanto, JMM no puede garantizar que la modificación de la variable var por "Thread 3" será visible para "Thread 2", aunque "Thread 3" ocurra antes que "Thread 2".

Escriba la operación en una variable volátil antes de cualquier operación en esta variable:

volatile int a;

//线程1
a = 1; //1

//线程2
b = a;  //2

Si el hilo 1 se ejecuta //, el "hilo 2" se ejecuta //, y después de que se ejecuta el "hilo 1", el "hilo 2" se ejecuta de nuevo, entonces se ajusta al "principio de suceder antes de volátil", por lo que el "hilo 2" en El valor de a debe ser 1.

** Si una operación ocurre antes de la operación B, la operación B ocurre antes de la operación C, luego ocurre una operación antes de la operación C: ** Si existe el siguiente bloque de código:

volatile int var;
int b;
int c;

//线程1
b = 4; //1
var = 3; //2

//线程2
c = var; //3
c = b; //4

Supongamos que "Thread 1" ejecuta // 2 este código y "Thread 2" ejecuta // 4 este código. Si el orden de ejecución es el siguiente:
// 1 // 2 // 3 // 4. Luego está la siguiente derivación (hd (a, b) significa un suceder antes de b):

Porque hay hd (// 1, // 2), hd (// 3, // 4) (principio de pasar antes de un solo subproceso)
y hd (// 2, // 3) (principio de pasar antes de los volátiles ))
Entonces hay hd (// 1, // 3), que se puede exportar hd (// 1, // 4) (transitividad del principio de pasar antes)
por lo que el valor de la variable c es finalmente 4
si el orden de ejecución es el siguiente:
// 1 // 3 // 2 // // 4 Entonces no se puede determinar el resultado de los últimos 4. La razón es que // 2 se ajusta directamente a cualquiera de los ocho principios anteriores y no se puede inferir nada a través de la transitividad.

A través de la explicación detallada de los cuatro principios anteriores, los cuatro principios guardados son más obvios.

Tres, resumen

Resumen: El principio de ocurre antes se refleja principalmente en las dos palabras clave volátiles y sincronizadas.

  • Volátil es la garantía de visibilidad proporcionada por JVM para variables compartidas en lectura y escritura de múltiples subprocesos. La función principal es prohibir que las variables compartidas modificadas volátiles se almacenen en caché (esto está relacionado con la caché de la CPU y el protocolo de coherencia de caché), y sin reordenar (Reordenamiento: optimización para mejorar el rendimiento en la situación actual en la que la velocidad de procesamiento de la CPU es mucho mayor que la velocidad de lectura y escritura de la memoria), pero la atomicidad de las operaciones de variables compartidas no está garantizada.
  • Sincronizado es un mecanismo de bloqueo proporcionado por la JVM, que garantiza la atomicidad, la visibilidad y el orden de las operaciones en la región bloqueada a través de las características de los bloqueos y las barreras de memoria.
  • La contención de bloqueo es el objeto (el bloqueo estático es el objeto de la clase, el bloqueo no estático es el objeto actual, es decir, el método de bloqueo bloquea el objeto personalizado) la "soberanía" de una parte de la memoria en el encabezado del objeto en la memoria del montón, solo un hilo puede adquirir la "soberanía", es decir, la exclusividad, y la atomicidad del funcionamiento del área bloqueada está garantizada a través de la exclusividad de la cerradura
  • Al agregar Load Barrier y Store Barrier antes y después del código, se puede garantizar la visibilidad de la operación de la variable compartida en el bloque o método de código bloqueado
  • Al agregar Adquirir barrera y Liberar barrera antes y después del código, puede garantizar el funcionamiento ordenado de la variable compartida en el bloque o método de código bloqueado

referencia:

https://www.cnblogs.com/tiancai/p/9636199.html

https://zhuanlan.zhihu.com/p/92341957

Terminado, ¡llámalo un día!

[ Difusión de conocimientos, valor compartido ], gracias amigos por su atención y apoyo, soy [ Zhuge Xiaoyuan ], un trabajador migrante de Internet que lucha en sus dudas.

Supongo que te gusta

Origin blog.csdn.net/wuxiaolongah/article/details/109733432
Recomendado
Clasificación