JMM是什么
- JMM(Java Memory Model)定义了Java虚拟机在计算机内存中的工作方式制度的一种规范
- 通过这组规范定义了程序中各个变量的访问方式(包括实例字段,静态字段,和构成数组对象的元素)
为什么要有JMM
- 不同的硬件生产商和不同的操作系统下,内存访问逻辑有一定的差异
- java内存模型就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果
模型图
- 主内存
- 共享区域,对应jvm的堆和方法区,所有线程都可访问
- 所有线程创建的对象都存放在主内存中
- 从更底层来说,主内存对应的就是物理内存
- 由于是共享数据区域,多条线程对同一个变量进行访问可能发生线程安全问题
- 工作内存
- 私有线程数据区域,对应jvm中的程序计数器,虚拟机栈,以及本地方法栈
- 工作内存对应的是栈中的部分区域
- 从更底层的来说,工作内存对应的是寄存器和高速缓存
- 私有信息,基本数据类型,直接分配到工作内存
工作方式
- 线程修改私有数据,直接在自己的工作空间当中修改
- 如果修改的是共享数据,把数据复制到工作内存当中修改,修改完后,刷新到主内存
为什么要这样设计
- JVM在设计时候考虑到,如果JAVA线程每次读取和写入变量都直接操作主内存,对性能影响比大
- 所以每条线程拥有各自的工作内存,工作内存中的变量是主内存中的一份拷贝
- 线程对变量的读取和写入,直接在工作内存中操作,而不能直接去操作主内存中的变量。
- 但是这样就会出现一个问题,当一个线程修改了自己工作内存中变量,对其他线程是不可见的,会导致线程不安全的问题。
- JMM制定了一套标准来保证开发者在编写多线程程序的时候,能够控制什么时候内存会被同步给其他线程。
多核cpu结构图
对于线程安全问题
- 总线加锁
- cpu在读取的时候不允许其它cpu进行读取,使得降低了cpu的吞吐量
- 缓存上的一致性协议MESI
缓存一致性协议MESI
- MESI中每个缓存行都有四个状态
- E(exclusive)(独占状态)
- S(shared)(共享状态)
- M(modified)(修改状态)
- I(invalid)(失效状态)
过程
- cpu在启动的时候, 会采用监听模式, 一直会监听消息的传递
- 如果在读取一个变量时, 发现被lock修饰时,其它CPU会监听到现在有人在读取数据
- 假设现在cpu1读取到一个变量a=1 ,是第一次读,会把这个变量a标记成E(独占状态)
- 如果此时,有另一个cpu也读取变量a,此时cpu1也会可监听到,并且会把状态更改为S状态 (共享状态) 在cpu2当中会也会标记成S状态
- 如果两个cpu都要对a变量进行修改
- 假设cpu1把a改为2 ,cpu2要把a改成3,会分别在cpu1当中的缓存行中加锁,一旦加锁成功后, 就可以来修改里面的内容,并且把状态标志成M(已修改状态 )'
- 假设cpu2缓存行加锁成功,会向消息总线发送一个本地写缓存 的消息,(如果两个人同时加锁,发消息给总线,此时总线就要采取内部仲裁的方式来决定谁先成功,通过总线的高低电位来裁决)
- 消息发成功后,会被cpu1捕捉到,cpu1会把自己当中的变量置为I(无效状态)到内存当中再读取最新的数据
- 在发出消息后, 并不是立马就写入到内存当中,会先把写的数据放到一个store buffer当中, 别cpu1把消息变为无效后, 才会写到入到内当中
-
当cpu1把消息设置会无效后, 会把原来的数据a=1放到一个queue队列当中,并且会发送一个消息通过已经置为无效