提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- JMM--Java Memory Model
- JMM 定义
- JMM规则:
- 线程间通信的步骤:
- JMM的三大特性:
- 原子性(Atomicity)
- 可见性:
- 有序性:
- JMM中的八种内存操作:
JMM–Java Memory Model
JMM 定义
JMM 是Java内存模型(Java Memory Model),简称JMM。它本身只是一个抽象的概念,并不真实存在,它描述的是一种规则或规范,
- 内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节
- JMM 和 JVM的内存结构,不是一个东西!!!!
JMM规则:
-
JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory)
-
线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。
-
不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。
全局描述。 在处理指令时,CPU会拉取数据,优先级是从L1到L2到L3,如果都没有,需要去主内存中拉取,JMM就是在CPU和主内存之间,来协调,保证可见、有序性等操作。
- CPU核心,就是CPU核心(寄存器)
缓存是CPU的缓存,CPU的缓存分为L1(线程独享),L2(内核独享),L3(多核共享) - JMM就是Java内存模型的核心,可见性,有序性都基于这实现。
- 主内存JVM,就是你堆内存。
线程间通信的步骤:
首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
然后,线程B到主内存中去读取线程A之前已更新过的共享变量。
本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。
- 线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。
- 当线程A和线程B需要通信时(如何激发?–隐式),线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。
- 随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。
从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。
JMM的三大特性:
Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的,那我们依次看一下这三个特征:
原子性(Atomicity)
一个操作不能被打断,要么全部执行完毕,要么不执行。在这点上有点类似于事务操作,要么全部执行成功,要么回退到执行该操作之前的状态。
基本类型数据的访问大都是原子操作,long 和double类型的变量是64位,但是在32位JVM中,32位的JVM会将64位数据的读写操作分为2次32位的读写操作来进行,这就导致了long、double类型的变量在32位虚拟机中是非原子操作,数据有可能会被破坏,也就意味着多个线程在并发访问的时候是线程非安全的。
可见性:
一个线程对共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量的这种修改(变化)。
Java内存模型是通过将在工作内存中的变量修改后的值同步到主内存,在读取变量前从主内存刷新最新值到工作内存中,这种依赖主内存的方式来实现可见性的。
有序性:
JMM中的八种内存操作:
为了支持 JMM,Java 定义了8种原子操作,用来控制主存与工作内存之间的交互:
-
read 读取:作用于主内存,将共享变量从主内存传送到线程的工作内存中。
-
load 载入:作用于工作内存,把 read 读取的值放到工作内存中的副本变量中。
-
store 存储:作用于工作内存,把工作内存中的变量传送到主内存中。
-
write 写入:作用于主内存,把从工作内存中 store 传送过来的值写到主内存的变量中。
-
use 使用:作用于工作内存,把工作内存的值传递给执行引擎,当虚拟机遇到一个需要使用这个变量的指令时,就会执行这个动作。
-
assign 赋值:作用于工作内存,把执行引擎获取到的值赋值给工作内存中的变量,当虚拟机栈遇到给变量赋值的指令时,就执行此操作。
-
lock锁定: 作用于主内存,把变量标记为线程独占状态。
-
unlock解锁: 作用于主内存,它将释放独占状态。