🦊 Java
1、Java基础
1.1 StringBuffer和StringBuilder的区别
1.2 ConcurrentHashMap和TreeMap实现原理
1.3 ArrayList和LinkedList实现原理
1.4 HashSet和TreeSet实现原理
1.5 深拷贝与浅拷贝
1.6 抽象类与接口
2、Java多线程
2.1 并发编程的三大特性
2.2 指令重排
2.3 Volatile原理
2.4 CAS原理
2.5 Java的4种引用级别
2.6 Java中的锁
2.7 Synchronized实现原理
2.8 线程池实现原理
2.9 AQS
2.10 创建线程的方式
2.11 ThreadLocal原理
3、JVM
3.1 判断对象是否存活的方法
3.2 JVM内存结构
3.3 常见的垃圾收集算法有哪些
3.4 指针碰撞和空闲列表
3.5 常见的垃圾收集器有哪些
3.6 内存溢出与内存泄漏的区别
3.7 常用的JVM启动参数有哪些
3.8 反射机制
4、NIO
4.1 概述
5、Spring
5.1 Spring IOC
5.2 Spring AOP
6、SpringBoot
6.1 SpringBoot、SpringCloud的联系与区别
-
+
tourist
register
Sign in
指令重排
在 `Java` 中看似顺序的代码在 `JVM` 中,可能会出现编译器或者 `CPU` 对这些操作指令进行了重新排序,在特定情况下,指令重排会给我们的程序带来不确定的结果。 ## 1 概念 在虚拟机层面,**为了尽可能减少内存操作速度远慢于 CPU 运行速度所带来的的 CPU 空置的影响,虚拟机会按照自己的一些规则将程序编写顺序打乱**,即**写在后面的代码在时间顺序上可能会先执行**,而**写在前面的代码可能会后执行**,以尽可能**充分的利用 CPU**,从 `Java` 源代码到最终实际执行的指令序列,会分别经历下面三种重排序: 1. **编译器优化重排序:** 编译器在**不改变单线程程序语义**的前提下,可以**重新安排语句的执行顺序**。 2. **指令级并行重排序:** 现代处理器采用了**指令级并行技术**来将多条指令重叠执行。如果**不存在数据依赖性**,处理器可以**改变语句对应机器指令的执行顺序**。 3. **内存系统重排序:** 处理器使用**缓存和读写缓冲区**,这使得**加载和存储操作看上去可能是在乱序执行**。 ![](https://luoyoubao.gitbooks.io/jvm/content/assets/20180413174357001.png) ## 2 数据依赖性 1. 如果**两个操作访问同一个变量**,且这两个操作中**有一个为写操作**,此时这两个操作之间就存在**数据依赖性**。 | 名称 | 代码示例 | 说明 | | ------ | ------------ | ---------------------------- | | 写后读 | a = 1;b = 1 | 写一个变量之后,再读这个变量 | | 写后写 | a = 1;a = 2; | 写一个变量之后,再写这个变量 | | 读后写 | a = b;b = 1; | 读一个变量之后,再写这个变量 | 2. 上面 3 中情况中,只要将两个操作的顺序进行重排序,程序的执行结果就会被改变。 3. **编译器和处理器**在重排序时,会**遵守数据依赖性**,编译器和处理器**不会改变存在数据依赖关系的两个操作的执行顺序**。不过这里所说的数据依赖性仅针对**单个处理器中执行的指令序列**和**单个线程中执行的操作**,**不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑**。 ## 3 as-if-serial 语义 ### 3.1 含义 1. `as-if-serial` 语义的意思是**不管怎么重排序,单线程的执行结果不能被改变**,编译器、`Runtime` 和处理器都必须遵守 `as-if-serial` 语义。 2. 为了遵守 `as-if-serial` 语义,编译器和处理器**不会对存在数据依赖关系的操作做重排序**,因为这种排序**会改变执行结果**,但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。 3. `as-if-serial` 语义把单线程程序保护了起来,遵守 `as-if-serial` 语义的编译器、`Runtime` 和处理器共同为编写单线程程序的程序员创建了一个幻觉:**单线程程序是按程序的顺序来执行的,无需担心重排序会干扰他们,也无需担心内存可见性问题**,但核心点还是**单线程**,**多线程情况下不遵守此原则**。 ### 3.2 示例 ```txt double pi = 3.14; // A double r = 1.0; // B double area = pi * r * r; // C ``` ![](https://luoyoubao.gitbooks.io/jvm/content/assets/20180928153343001.png) 1. 如图所示,`A` 和 `C` 之间存在数据依赖关系,同时 `B` 和 `C` 之间也存在数据依赖关系。 2. 因此,在最终执行的指令序列中,`C` 不能被重排序到 `A` 和 `B` 前面,但是 `A` 和 `B` 之间没有数据依赖关系,编译器和处理器可以对 `A` 和 `B` 之间的执行顺序进行重排序。 ![](https://luoyoubao.gitbooks.io/jvm/content/assets/20180928153443001.png) ## 4 happens-before 规则 ### 4.1 定义 1. 如果一个操作 `happens-before` 另一个操作,那么第一个操作的**执行结果**将对第二个操作**可见**,而且第一个操作的**执行顺序**排在第二个操作**之前**。 2. 两个操作之间存在 `happens-before` 关系,并不意味着一定要按照 `happens-before` 原则制定的顺序来执行,如果**重排序之后的结果与按照 `happens-before` 关系来执行的结果一致**,那么**这种重排序并不非法**。 ### 4.2 规则 1. **程序次序规则:**一个线程内,按照代码顺序,书写在**前面的操作先行发生于**书写在**后面的操作**。 2. **锁定规则:** 一个 `unlock` 操作**先行发生于**后面对**同一个锁的 `unlock` 操作**。 3. **`volatile` 变量规则:** 对一个变量的**写操作先行发生于**后面对这个变量的**读操作**。 4. **传递规则:** 如果操作 `A` 先行发生于操作 `B`,而操作 `B` 又先行发生于操作 `C`,则可以得出操作 `A` 先行发生于操作 `C`。 5. **线程启动规则:**`Thread` 对象的 `start()` 方法**先行发生于此线程的每一个操作**。 6. **线程中断规则:** 对线程 `interrupt()` 方法的调用**先行发生于被中断线程的代码检测到中断事件的发生**。 7. **线程终结规则:** 线程中所有的操作都**先行发生于线程的中止检测**。 8. **对象终结规则:** 一个对象的**初始化完成先行发生于他的 `finalize()` 方法的开始**。 ## 5 内存屏障 ### 5.1 含义 1. **内存屏障**(Memory Barrier)是**硬件之上,操作系统或 `JVM` 之下,对并发做出的最后一层支持,它是一种标准,不同的厂商会采用不同的实现**。 2. 通过 `volatile` 标记,可以解决**编译器层面的可见性与重排序的问题**,而内存屏障则解决了**硬件层面的可见性与重排序问题**。 ### 5.2 标准 > 1. Store:将处理器缓存的数据**刷新到内存**中。 > 2. Load:将内存存储的数据**拷贝到处理器的缓存**中。 | 屏障类型 | 指令示例 | 说明 | | ------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | LoadLoad Barriers | Load1;LoadLoad;Load2 | 该屏障确保 Load1 数据的装载先于 Load2 及其后所有装载指令的操作。 | | StoreStore Barriers | Store1;StoreStore;Store2 | 该屏障确保 Store1 立刻刷新数据到内存的操作先于 Store2 及其后所有存储指令的操作。 | | LoadStore Barriers | Load1;LoadStore;Store2 | 确保 Load1 的数据装载先于 Store2 及其后所有的存储指令刷新数据到内存的操作。 | | StoreLoad Barriers | Store1;StoreLoad;Load2 | 该屏障确保 Store1 立即刷新数据到内存的操作先于 Load2 及其后所有装载指令的操作,他会使该屏障之前的所有内存访问指令完成之后,才执行该屏障之后的内存访问指令。 | `StoreLoad Barriers` 同时具备其他三个屏障的效果,因此也被称之为**全能屏障**(mfence),是目前大多数处理器所支持的,但是相对其他屏障,该屏障的开销相对昂贵。 ## 6 参考文献 1. [JVM 之指令重排分析](https://blog.csdn.net/blueheart20/article/details/52117761)。 2. [数据依赖性](https://luoyoubao.gitbooks.io/jvm/content/javanei-cun-mo-xing/shu-ju-yi-lai-xing.html)。 3. [指令重排序](https://luoyoubao.gitbooks.io/jvm/content/chapter1/zhi-ling-zhong-pai.html)。 4. [【死磕 Java 并发】-----Java 内存模型之 happens-before](https://www.cnblogs.com/chenssy/p/6393321.html)。 5. [一文解决内存屏障](https://monkeysayhi.github.io/2017/12/28/%E4%B8%80%E6%96%87%E8%A7%A3%E5%86%B3%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C)。
ricear
July 11, 2022, 10:08 p.m.
©
BY-NC-ND(4.0)
转发文档
Collection documents
Last
Next
手机扫码
Copy link
手机扫一扫转发分享
Copy link
Markdown文件
share
link
type
password
Update password