Java内存模型综述

383 阅读7分钟

目录


1、Java内存模型的基础
2、Java内存模型中的顺序一致性
3、Java内存模型中的happens-before
4、同步原语(volatile、synchronized、final)
5、双重检查锁定与延迟初始化
6、Java内存模型综述

处理器的内存模型


顺序一致性内存模型是一个理论参考模型,JAVA内存模型和处理器在设计时通常会以顺序一致性内存模型作为参考。在设计时,JAVA内存模型和处理器内存模型会对顺序一致性模型做一些放松,因为如果完全按照顺序一致性来实现处理器和JAVA内存模型,那么很多处理器和编译器优化都要被禁止,这对执行性能会有很大的影响。

根据对不同类型的读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为以下几种类型:

1)放松程序中写-读操作的顺序,由此产生了 Total Store Ordering内存模型(简称TSO)
2)在上面的基础上,继续放松程序中写-写操作的顺序,由此产生了 Partail Store
Order内存模型(简称PSO) 3)在前面两条的基础上,继续放松程序中读-写和读-读操作的顺序,由此产生了Relaxed Memory Order内存模型(简称为RMO)和PowerPC内存模型。

注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因 为处理器要遵守as-if-serial语义处理器不会对存在数据依赖性的两个内存操作做重排序)。

表展示了常见处理器内存模型的细节特征如下:

从表中可以看到,所有处理器内存模型都允许写-读重排序,原因:它们都使用了写缓存区。写缓存区可能导致写-读橾作重排序。同时,我们可以看到这些处 理器内存模型都允许更早读到当前处理器的写,原因同样是因为写缓存区。由于写缓存区仅 对当前处理器可见,这个特性导致当前处理器可以比其他处理器先看到临时保存在自己写缓 存区中的写。

表中的各种处理器内存模型,从上到下,模型由强变弱。越是追求性能的处理器,内 存模型设计得会越弱。因为这些处理器希望内存模型对它们的束缚越少越好,这样它们就可 以做尽可能多的优化来提高性能。

由于常见的处理器内存模型比JMM要弱,Java编译器在生成字节码时,会在执行指令序 列的适当位置插入内存屏障来限制处理器的重排序。同时,由于各种处理器内存模型的强弱 不同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中 需要插入的内存屏障的数量和种类也不相同。图3-48展示了JMM在不同处理器内存模型中需 要插入的内存屏障的示意图。

JMM屏蔽了不同处理器内存模型的差异,它在不同的处理器平台之上为Java程序员呈现 了一个一致的内存模型.

各种内存模型之间的关系


JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存 模型是一个理论参考模型。下面是语言内存模型、处理器内存模型和顺序一致性内存模型的 强弱对比示意图如图所示。

从图中可以看出:常见的4种处理器内存模型比常用的3中语言内存模型要弱,处理器内存 模型和语言内存模型都比顺序一致性内存模型要弱。同处理器内存模型一样,越是追求执行 性能的语言,内存模型设计得会越弱。

Java内存模型的内存可见性保证


按程序类型,Java程序的内存可见性保证可以分为下列3类。

·单线程程序。单线程程序不会出现内存可见性问题。编译器、runtime和处理器会共同确 保单线程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。

正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行 结果与该程序在顺序一致性内存模型中的执行结果相同)。这是JMM关注的重点,JMM通过限 制编译器和处理器的重排序来为程序员提供内存可见性保证。

未同步/未正确同步的多线程程序。JMM为它们提供了最小安全性保障:线程执行时读取 到的值,要么是之前某个线程写入的值,要么是默认值(0、null、false)。

注意,最小安全性保障与64位数据的非原子性写并不矛盾。它们是两个不同的概念,它 们“发生”的时间点也不同。最小安全性保证对象默认初始化之后(设置成员域为0、null或

false), 才会被任意线程使用。最小安全性”发生“在对象被任意线程使用之前。64位数据的非 原子性写“发生“在对象被多个线程使用的过程中(写共享变量)。当发生问题时(处理器B看到 仅仅被处理器A"写了一半"的无效值),这里虽然处理器B读取到一个被写了一半的无效值,但 这个值仍然是处理器A写入的,只不过是处理器A还没有写完而已。最小安全性保证线程读取 到的值,要么是之前某个线程写入的值,要么是默认值(0、null、false)但最小安全性并不保证 线程读取到的值,一定是某个线程写完后的值。最小安全性保证线程读取到的值不会无中生有的冒出来,但并不保证线程读取到的值一定是正确的。

图展示了这3类程序在JMM中与在顺序一致性内存模型中的执行结果的异同。

只要多线程程序是正确同步的,JMM保证该程序在任意的处理器平台上的执行结果,与 该程序在顺序一致性内存模型中的执行结果一致。

对旧内存模型的修补


JSR-133对JDK5之前的旧内存模型的修补主要有两个。

1)增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格 限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。
2)增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。为 此,JSR-133为final增加了两个重排序规则。在保证final引用不会从构造函数内逸出的情况下, final具有了初始化安全性。

如果有需要的话可以关注一下我的公众号,会即时更新Java相关技术文章,公众号内还有一些实用资料,如Java秒杀系统视频教程、黑马2019的教学资料(IDEA版)、BAT面试题汇总(分类齐全)、MAC电脑常用安装包(有一些是淘宝买的,已PJ的)。