java

Java内存模型

3.1Java内存模型基础

3.1.1并发编程模型的两个关键问题
  • 线程之间如何通信
  • 线程之间如何同步

Java的并发采用的共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。

如果编写多线程程序的java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

3.1.2Java内存模型的抽象结构

在Java中,所有实例域、静态域和数组元素都存在堆内存中,堆内存在线程之间共享。 局部变量,方法定义参数,异常处理参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存 模型的影响。

Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。 线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程 以读/写共享变量的副本。 本地内存是JMM的一个抽象概念,并不是真实存在,它涵盖了缓存、写缓存冲区、寄存器以及其他的硬件和 编译优化。

image

从图3-1来看,如果线程A与线程B之间要通信的话,必须要经历下面2个步骤。 1)线程A把本地内存A中更新过的共享变量刷新到主内存中去。 2)线程B到主内存中去读取线程A之前已更新过的共享变量。

3.1.3 从源代码到指令序列的重排序

在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。

JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁 止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

3.1.4 并发编程模型的分类

现代的处理器使用写缓冲区临时保存向内存写入的数据。写缓冲区可以保证指令流水线 持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以 批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总 线的占用。虽然写缓冲区有这么多好处,但每个处理器上的写缓冲区,仅仅对它所在的处理器 可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行 顺序,不一定与内存实际发生的读/写操作顺序一致!

3.1.5 happens-before简介

与程序员密切相关的happens-before规则如下。

  • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
  • 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的 读。
  • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。

关于作者

程序员,软件工程师,java, golang, rust, c, python,vue, Springboot, mybatis, mysql,elasticsearch, docker, maven, gcc, linux, ubuntu, centos, axum,llm, paddlepaddle, onlyoffice,minio,银河麒麟,中科方德,rpm