Diario de estudio "Comprensión profunda de la máquina virtual JVM" Capítulo 12 Modelo de memoria y subprocesos de Java

Visión de conjunto

La velocidad de cómputo del procesador de la computadora está demasiado lejos de la velocidad de sus sistemas de almacenamiento y subcomunicación, lo que hace que el procesador pase la mayor parte del tiempo en un estado de espera inactivo . Si no desea desperdiciar los recursos del procesador, debe encontrar una manera de exprimir el procesador. La multitarea se considera un medio muy eficaz de "exprimir".

1. Eficiencia y consistencia del hardware

Dado que la velocidad de cómputo entre el procesador y la memoria es diferente en varios órdenes de magnitud, las computadoras modernas tienen que agregar una o más capas de cachés cuyas velocidades de lectura y escritura pueden acercarse a la velocidad de cómputo del procesador como interfaz entre el procesador y la memoria. Búfer entre: copia los datos necesarios para la operación desde la memoria a la memoria caché, de modo que la operación se pueda realizar rápidamente y, una vez completada la operación, se sincroniza de nuevo con la memoria desde la memoria caché, de modo que el procesador no No tiene que esperar a que la memoria lenta lea y escriba.

En los sistemas multiprocesador, esta estructura también plantea un nuevo problema, ¡la coherencia de caché! Hay varios procesadores en una computadora, cada uno con su propio caché, que a su vez comparten la misma memoria principal. Cuando las tareas informáticas de múltiples procesadores involucran la misma área de memoria principal, los datos de caché respectivos pueden ser inconsistentes. Por lo tanto, es necesario formular un protocolo de coherencia de caché correspondiente para garantizar que los datos de caché de cada procesador sean consistentes.

imagen.png

Además de aumentar la memoria caché, para maximizar la utilización de la unidad aritmética dentro del procesador, el procesador puede realizar una optimización de ejecución fuera de orden en el código de entrada, y el ejecutor reorganizará el resultado de la ejecución fuera de orden. ejecución para garantizar que el resultado sea el mismo que Los resultados de la ejecución secuencial son los mismos, pero la secuencia no es necesariamente la misma que la secuencia del código.

2. Modelo de memoria Java

Se utiliza para proteger las diferencias entre varios hardware y sistemas operativos, de modo que los programas Java puedan lograr efectos de acceso a la memoria consistentes en varias plataformas.

El modelo de memoria de Java estipula que todas las variables se almacenan en la memoria principal (no en la memoria del hardware, sino en parte de la memoria de la máquina virtual), además de que cada subproceso tiene su propia memoria de trabajo (similar a la memoria caché del procesador). La memoria de trabajo de un subproceso guarda una copia de la memoria principal de la variable utilizada por el subproceso.Todas las operaciones del subproceso en la variable deben realizarse en la memoria de trabajo y no pueden leer y escribir datos directamente en la memoria principal, y no hay conexión directa entre diferentes hilos.Para acceder a las variables de otros hilos, la transmisión de valores variables entre hilos debe completarse a través de la memoria principal.La relación entre el hilo, la memoria principal y la memoria de trabajo es se muestra en la figura.

imagen.png

3. Interacción entre la memoria

Con respecto a los detalles de implementación de cómo copiar variables de la memoria principal a la memoria de trabajo y cómo sincronizar desde la memoria de trabajo a la memoria principal, las siguientes ocho operaciones se definen en el modelo de memoria de Java para completar.

lock: 作用于主内存变量,把一个变量标识为一条线程独占的状态。
unlock:作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read:作用于主内存的变量,它把一个变量的值从主内存中传输到线程的工作内存中,以便随后的load动作使用。
load:作用于工作内存的变量,把read操作从主内存中得到的变量值放入到工作内存的变量副本中
use:作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用给变量赋值的字节码指令时执行这个操作。
assign:作用于工作内存变量,把从执行引擎中接收的值赋值给工作内存变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store:作用于工作内存变量,把工作内存中变量的值传送到主内存中,以便随后的write操作使用。
write:作用于主内存中的变量,把store操作从工作内存中得到的变量值放入到主内存变量中。 
复制代码

Reglas especiales para variables volátiles

  1. Visibilidad garantizada de las variables compartidas.

Visibilidad significa que cuando un subproceso modifica el valor de esta variable, el nuevo valor es inmediatamente visible para otros subprocesos. Sin embargo, las variables ordinarias no pueden hacer esto.Cuando el valor de las variables ordinarias se pasa entre subprocesos, debe completarse a través de la memoria principal.

  1. Deshabilitar el reordenamiento de instrucciones

Cuando una variable con la palabra clave volátil se compila en el código de bytes de Java, se agregará una operación de bloqueo cuando se modifique, lo que equivale a una barrera de memoria.

Tres características de la concurrencia

atomicidad

Solo un subproceso puede ejecutarse al mismo tiempo en un cierto período de tiempo, y otros subprocesos no pueden ejecutarse, es decir, el bloque de código después del "bloqueo".

visibilidad

Cuando un subproceso modifica el valor de una variable compartida, otros subprocesos se dan cuenta inmediatamente de la modificación.

orden

El orden natural en los programas de Java se puede resumir en una oración: si se observa en este hilo, todas las operaciones están ordenadas, y si se observa un hilo en otro hilo, todas las operaciones están desordenadas. El fenómeno del reordenamiento de instrucciones que ocurre cuando el procesador se asegura de que el resultado de la operación sea correcto ya se ha introducido anteriormente.

Pasar antes del principio

  • reglas de orden del programa

Dentro de un subproceso, las operaciones escritas antes ocurren antes que las operaciones escritas más tarde, en orden de flujo de control.

  • Supervisar las reglas de bloqueo

Una operación de desbloqueo ocurre primero antes de una operación de bloqueo subsiguiente en el mismo bloqueo.

  • reglas de variables volátiles

Se produce una escritura en una variable volátil antes de una lectura posterior de la variable.

  • regla de inicio de hilo

El método Thread.start() precede cada acción que ocurre en este hilo.

  • reglas de terminación de subprocesos

Todas las operaciones en un subproceso ocurren antes de la detección de terminación para este subproceso.

  • transitividad

如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

Java语言无须使用任何同步手段保障就能够成立的先行发生规则有且只有上面这些。

线程的实现方式

内核线程实现

内核线程是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上,每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情。

程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程,由于每一个轻量级 进程都由一个内核线程支持,因此只有先支持内核线程才能有轻量级进程。

用户线程实现

广义上来讲,一个线程只要不是内核线程,都可以认为是用户线程。狭义上用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知到用户线程的存在及如何实现的。用户线程的建立,同步,销毁和调度完全在用户态中完成,不需要内核的帮助。

混合实现(Java线程实现方式)

线程除了依赖内核线程实现和完全由用户线程自己实现之外,还有一种将内核线程和用户线程一起使用的方式。这种混合实现的方式下,既有用户线程,又有内核线程。用户线程还是完全建立在用户空间中,因此用户线程的创建,切换等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统支持的轻量级进程则可以作为用户线程和内核线程之间的桥梁,降低整个进程被阻塞的风险。

内核线程的调度成本为什么高?

其成本主要来自用户态和内核态之间的状态转换,而这两种状态转换的开销主要来自于响应中断、保护和恢复执行现场的成本。

总结

  1. 处理器进行运算时不是直接和内存进行交互处理数据的,而是在处理器的高速缓存中进行运算处理,处理完成后再同步回内存。
  2. El modelo de memoria de Java es un conjunto de conceptos abstractos que en realidad no existen y describe cómo se accede a cada variable del programa. La memoria principal corresponde al área de métodos y al área de almacenamiento dinámico, y la memoria de trabajo es la pila de la máquina virtual Java y la pila de métodos nativos.
  3. Las dos funciones principales de volatile: prohibir el reordenamiento de instrucciones y garantizar la visibilidad de las variables compartidas
  4. Las últimas tres formas de implementar subprocesos, los subprocesos de Java se implementan utilizando el método de subproceso de núcleo + subproceso de usuario.
  5. El principio de "sucede antes" en los programas Java.
  6. ¿Por qué el cambio del modo usuario al modo kernel afecta el rendimiento?

Supongo que te gusta

Origin juejin.im/post/7079384045334986782
Recomendado
Clasificación