Java内存模型
为什么要有内存模型
先来说计算机的内存模型,Memory Model。
刚开始数据放在物理内存中
计算机执行程序,每条指令在CPU中执行,数据存放在主存中。
慢慢得,内存跟不上CPU技术的发展,内存的读写成为了计算机处理的瓶颈
所以,人们想出一个方法,在CPU和内存之间增加高速缓存
之后,当程序运行过程中,将运算需要的数据,从主存中复制一份到CPU的高速缓存(数据拷贝),CPU进行计算时直接从高速缓存中读取数据、写入数据,运算结束后,再将高速换算中的数据刷新到主存当中。
再后来,CPU进一步发展,缓存已经无法满足,衍生出多机缓存。
当CPU要读取一个数据时,首先从一级缓存中查找,如果没有,依次再从二级缓存、三级缓存、内存中查找
单核CPU只有一套L1,L2,L3缓存
多核CPU,每核都有一套L1缓存(甚至L2),并共享L3缓存(可能L2)
什么是JMM
内存模型:在特定的操作协议下,对特定的内存或者高速缓存进行读写访问的过程抽象描述,不同架构下的物理机拥有不一样的内存模型。
Java虚拟机是一个跨平台的虚拟系统,它的内存模型,即Java内存模型,Java Memory Model,JMM
JMM是一种规范,规定了:
- 所有变量都存储在主内存中
- 每个线程有自己的工作内存,保存了该线程所需变量在主内存中的副本拷贝
- 线程对变量的所有操作都在工作内存中进行,不能直接读写主内存
- 不同线程之间,值得传递都通过主内存完成
在Java中,上述的变量指代:实例域、静态域和数组元素,它们存储在堆内存中,堆内存在线程之间共享。
而局部变量、方法定义参数、异常处理参数不会在线程之间共享,所以它们不会有内存可见性问题,不受内存模型影响
上图中的工作内存(本地内存)是JMM的一个抽象概念,涵盖了缓存、写缓存区、寄存器等硬件。
八种原子操作
JMM规定线程对共享变量的操作细分为八种,这些操作再也细分不了,所以称之为原子操作。
- read,读取
从主内存的共享变量中读取数据,以便后续的load操作使用 - load,载入
将第一步读取的数据,存放到工作内存中,建立共享变量的副本 - use,使用
将工作内存中的副本数据交由执行引擎处理,每当虚拟机执行字节码指令,需要使用到相关变量就会执行这个操作 - assign,赋值
将执行结果赋值回数据副本,每当虚拟机遇到一个给变量赋值的字节码指令时使用这个操作 - store,存储
从副本中读取数据,以便后续的write操作使用 - write,写入
将上一步读取的数据写回共享变量
所以,如果某个线程下,数据还没来的及写回,而其他线程已经开始使用,会导致多线程的并发问题。
为了解决这个问题而设计了另外各种原子操作:
- lock,锁定
对共享变量进行加锁(标记为某一线程的独占状态),其他线程只能进入等待队列 - unlock,解锁
对共享变量进行解锁,其他线程进行竞争,获取锁对共享变量再次加锁的线程才能进入可执行状态