美文网首页
JAVA 内存模型(JMM)

JAVA 内存模型(JMM)

作者: 嗷嗷待哺丶 | 来源:发表于2021-10-14 08:19 被阅读0次

Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

一、内存屏障

在CPU中,每个CPU又有多级缓存,一般分为L1,L2,L3。

CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找,每个cpu有且只有一套自己的缓存。

因为这些缓存,提高了数据访问性能,避免每次都向主内存索取,但是弊端也很明显,不能实时的和内存发生信息交换,涉及到一个缓存一致性的问题,而且由于操作系统可能存在重排序,导致读取到错误的数据,因此,操作系统提供了一些内存屏障以解决这种问题.

不同硬件对内存屏障的实现方式不一样,java屏蔽掉这些差异,通过jvm生成内存屏障

1. 内存屏障的作用

cpu执行指令可能是无序的,它有两个比较重要的作用

  1. 阻止屏障两侧指令重排序。
  2. 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。

2. volatile关键字

volatile 保证了内存可见性并且禁止CPU指令重排序

可见性:指线程之间的可见性,一个线程修改的状态对另一个线程是可见的,也就是一个线程修改的结果,另一个线程马上就能看到。

指令重排序: JVM为了优化指令、提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度;指令重排序包括编译器重排序和运行时重排序。

注意:volatile不能保证数据的原子性。

举个🌰 ,有一个变量 i ,被volatile修饰。两个线程想对这个变量修改,都对其进行自增操作,也就是 i++,这个过程可以分为三步:

  1. 获取i的值
  2. 对i的值进行加1
  3. 最后将得到的新值写回到缓存中

假设线程A首先获取到了i的初始值100,但是还没来得及修改,就阻塞了,这时线程B开始了,它也得到了i的值,由于i的值未被修改,那么线程B得到的值也是100,之后对其进行加1操作,得到101后,将新值写入到缓存中,再刷入主内存中。根据可见性的原则,这个主存的值可以被其他线程可见。

那么问题就来了,线程A已经读取到了i的值为100,线程A阻塞结束后,继续将100这个值加1,得到101,再将值写到缓存,最后刷入主内存,数据就不正确了。

所以即便是volatile具有可见性,也不能保证被它修饰的变量具有原子性。

对于禁止指令重排序,在 JAVA 设计模式之《单例模式》 中,双重检查锁方式里详细讲解了。

相关文章

网友评论

      本文标题:JAVA 内存模型(JMM)

      本文链接:https://www.haomeiwen.com/subject/vxdeoltx.html