原来规避重排序是为了保障可见性

简介: 导致可见性的原因有很多,如:1. 为了提升性能而实施的编译期重排序。2. 数据在寄存器中。3. cpu缓存的更改未同步到主内存中 或 内存中的更改未同步到cpu缓存(运行期重排序)。
我是石页兄,朋友不因远而疏,高山不隔友谊情;偶遇美羊羊,我们互相鼓励

欢迎关注微信公众号「架构染色」交流和学习

一、了解重排序

为了性能,编译时和运行时都会有重排序,造成指令执行顺序变了,宏观上从这3点了解重排序:

  1. 线程内有序:如果再本线程内观察,所有的操作都是有序的,即线程内表现为串行的语义(Within Thread As-If-Serial Semantics)。
  2. 线程间无序:如果再一个线程中观察另一个线程,所有的操作都是无序的,即 指指令重排序现象和工作内存与主内存同步延迟现象。
  3. 总会有重排序:指令重排序在任何时候都有可能发生,与是否为多线程无关,之所以在单线程下感觉没有发生重排序,是因为线程内表现为串行的语义的存在。

二、重排序遵守 as-if-serial语义

as-if-serial语义 : 不管怎么重排序(编译器和处理器为了提高并行度),程序在单线程中的执行结果不会改变。

编译器、runtime和处理器都必须遵守as-if-serial语义:

  1. 为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。
  2. 如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序

正确的理解as-if-serial语义的功效:

  1. 如果在本线程内观察,所有的操作都是有序的;
  2. 如果在一个线程中观察另一个线程,所有的操作都是无序的。

三、重排序的类型

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

  1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。属于编译期重排序。
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。属于运行期重排序。
  3. 内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行,属于运行期重排序。

image.png

从编译运行视角分为两类:

  1. 编译期重排序:包括 编译器优化的重排序。
  2. 运行期重排序:包括 指令级并行的重排序,内存系统的重排序。

四、为何要禁止重排序

修复Java内存模型,第2部分里的一段话开始思考

理解JMM所需的关键概念之一是可见性 -您如何知道如果线程A执行someVariable = 3,其他线程将看到线程A在其中 写入的值3?存在许多原因,为什么另一个线程可能不会立即为以下值看到值3 someVariable:可能是因为编译器已对指令进行了重新排序以便更有效地执行,或者已将someVariable其缓存在寄存器中,或者将其值写入了缓存在写处理器上但尚未刷新到主内存,或者在读处理器的缓存中有旧的(或陈旧的)值。内存模型确定线程何时可以可靠地“看到”对其他线程所做的变量的写入。特别是,内存模型为定义了语义volatile,synchronized,final这确保了跨线程的内存操作可见性。

从其原文中不难看出,导致可见性的原因有很多

  1. 为了提升性能而实施的编译期重排序。
  2. 数据在寄存器中。
  3. cpu缓存的更改未同步到主内存中 或 内存中的更改未同步到cpu缓存(运行期重排序)。
  4. 。。。

其中两次提到重排序会导致可见性问题。那么某些情况下为了保证可见性则需禁止重排序

五、最后说一句

我是石页兄,如果这篇文章对您有帮助,或者有所启发的话,欢迎关注笔者的微信公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。

欢迎点击链接扫马儿关注、交流。

相关文章
|
5月前
|
安全 Java
7.volatile怎么通过内存屏障保证可见性和有序性?
7.volatile怎么通过内存屏障保证可见性和有序性?
30 0
7.volatile怎么通过内存屏障保证可见性和有序性?
|
5月前
|
前端开发 JavaScript
竞态问题:深入理解与解决方案
竞态问题:深入理解与解决方案
|
7月前
|
Java 编译器 程序员
JMM的内存可见性保证
JMM的内存可见性保证
28 0
|
9月前
|
安全 Java
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
|
12月前
|
存储 缓存 安全
并发三要素 : 可见性, 原子性, 有序性
并发三要素:可见性, 原子性, 有序性,并发问题该怎样解决,怎样实现数据同步,这篇文章为您解决
66 0
|
缓存 安全 Java
Java并发编程 - volatile 怎么保障内存可见性 & 防止指令重排序?
Java并发编程 - volatile 怎么保障内存可见性 & 防止指令重排序?
119 0
Java并发编程 - volatile 怎么保障内存可见性 & 防止指令重排序?
|
存储 设计模式 Java
对内存可见性造成影响的代码
我们在开发过程中,是不是频繁的写一些System.out.println()来验证程序的执行?切记在正式环境将打印语句去除!
95 2
对内存可见性造成影响的代码
synchronized 优化手段之锁膨胀机制!(3)
synchronized 优化手段之锁膨胀机制!(3)
116 0
synchronized 优化手段之锁膨胀机制!(3)
|
安全 Java 程序员
synchronized 优化手段之锁膨胀机制!(1)
synchronized 优化手段之锁膨胀机制!(1)
110 0
synchronized 优化手段之锁膨胀机制!(1)
|
Java
synchronized 优化手段之锁膨胀机制!(4)
synchronized 优化手段之锁膨胀机制!(4)
99 0