Java内存模型(JMM)是什么?
什么是JMM?
在了解JMM之前,先了解下为什么提出了JMM。
- CPU缓存,在多核CPU的情况下,带来了可见性问题
- 操作系统对当前执行线程的切换,带来了原子性问题
- 编译器指令重排优化,带来了有序性问题。
JMM 即Java内存模型,也就是Java Memory Model,简称JMM,本身是一种抽象的概念,实际上并不存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式。
JMM核心思想
Java 内存模型规定,将所有的变量都存放在主内存中,当线程使用变量时,会把主内存里面的变量复制到自己的工作空间或者叫做工作内存,线程读写变量时操作的是自己工作内存中的变量。即:
- 线程解锁前,必须把共享变量的值刷新回主内存
- 线程加锁前,必须读取主内存中的最新值到自己的工作内存
- 加锁和解锁必须是同一把锁
实际中现成的工作内存如图所示,一个双核CPU系统架构,每个核有自己的控制器和运算器,其中控制器包含一组寄存器和操作控制器,运算器执行算术逻辑运算。每个核都有自己的一级缓存,在有些架构里面还有一个所有CPU都共享的二级缓存。那么Java内存模型里面的工作内存,就对应这里的L1或者L2缓存或者CPU的缓存。
假如线程A和线程B同时处理一个共享变量,会出现什么情况,如上图所示的CPU架构,假设线程A和线程B使用不同CPU执行,并且当前两级Cache都为空,那么这时候由于Cache的存在,将会导致内存不可见问题。
- 线程A首先获取共享变量的值,由于两级Cache都没有命中,所以会去主内存中加载共享变量的值,假如为0,就会把0存到两级缓存,线程修改值为1,然后将其写入两级Cache,并且刷新回主内存,线程A操作完毕后,线程A所在的两级Cache和主内存的值都是1。
- 线程B获取共享变量的值,首先一级缓存没有命中,然后查看二级缓存,二级缓存命中了,所以返回1;到目前都是正常的,因为主内存的值也是1.然后线程B修改值为2,并将其存放到线程2的一级缓存和二级缓存,最后更新主内存的值为2.到这里也是一切正常。
- 线程A此时有需要修改共享变量的值,获取一级缓存命中值为1,这就问题出现了,线程B已经将值修改为了2,而线程A拿到的值依然是1,这就是共享变量内存不可见问题。
解决共享变量内存不可见问题,可以使用java中的volatile关键字(另一篇文章有介绍)。