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
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
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
- 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
- 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.
- La secuencia del programa se ejecuta en flujo de secuencia dentro de un solo hilo
- El bloqueo del tubo (bloqueo del monitor) desbloquea un bloqueo antes de que se bloquee
- El principio volátil escribe la variable volátil antes de leer la variable
- Principio de inicio de subproceso El método de inicio de subproceso start () se ejecuta antes de todas las operaciones en el subproceso
- 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
- 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
- Principio de finalización de objeto El método de construcción de un objeto precede a su método de finalización
- 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.
- memoria = asignar () // asignar memoria
- initclass (memoria) // inicializar objeto
- 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
- No permitir la inicialización de objetos y reordenar objetos puntiagudos
Use la semántica de Volatile para
agregar la descripción de Volatile
- 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
-
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.
-
Nivel de variable volátil, variable de método sincronizado
-
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.
-
Volátil no bloqueará, sincronizado bloqueará
-
la variable volatitle no se optimizará, la sincronización se optimizará