深入了解volatile、内存屏障与happens-before规则

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

我们来看加了volatile前后的代码,用的就是阿里规约提供给我们的双重检查锁的代码。我们分别编译了两次,第一个是没有使用volatile关键字修饰的,第二个是使用volatile关键字来修饰,然后取出他们的的汇编代码(实在是设计的地方太底层,其实这里算是用到了策略模式了)

未使用volatile修饰

0x000000010d29e93b: mov %rax,%r10
0x000000010d29e93e: shr $0x3,%r10
0x000000010d29e942: mov %r10d,0x68(%rsi)
0x000000010d29e946: shr $0x9,%rsi
0x000000010d29e94a: movabs $0xfe403000,%rax
0x000000010d29e954: movb $0x0,(%rsi,%rax,1)

使用volatile修饰

0x0000000114353959: mov %rax,%r10
0x000000011435395c: shr $0x3,%r10
0x0000000114353960: mov %r10d,0x68(%rsi)
0x0000000114353964: shr $0x9,%rsi
0x0000000114353968: movabs $0x10db6e000,%rax
0x0000000114353972: movb $0x0,(%rsi,%rax,1)
0x0000000114353976: lock addl $0x0,(%rsp)

很明显,在movb操作后,加了volatile修饰的汇编代码后面多了一条汇编指令lock addl $0x0,(%rsp),这个操作相当于一个内存屏障,指令重排时不能把后面的指令重排序到内存屏障之前的位置。lock前缀会强制执行原子操作,它的作用是是的本CPU的cache写入了内存,该写入动作会引起别的CPU无效化其cache。所以通过这样一个空操作,可让前面volatile变量的便是对其他CPU可见

从硬件架构上讲,指令重排序是指CPU将多条指令不按程序规定的顺序分开发送给各相应的点,但并不是指令任意重排,CPU需要能正确处理指令,以保障程序能得出正确的执行结果。lock addl $0x0,(%rsp) 指令把修改同步到内存时,意味着所有值钱的操作都已经执行完成,这样便形成了指令重排序无法越过内存屏障的效果。

三、内存屏障

既然指令重排和可见性都依赖了lock,同时lock指令引出了内存屏障,我们就来学习一下什么是内存屏障。

1、定义

内存屏障:保证屏障前的读写指令必须在屏障后的读写指令之前执行,通知被Volatile修饰的值,每次读取都从主存中读取,每次写入都同步写入主存。

内存屏障具体又分为写屏障和读屏障 写屏障(Store Memory Barrier):强制将缓存中的内容写入到缓存中或者将该指令之后的写操作写入缓存直到之前的内容被刷入到缓存中,也被称之为smp_wmb 读屏障(Load Memory Barrier):强制将无效队列(volatile写操作之后失其作废)中的内容处理完毕,也被称之为smp_rmb

屏障类型指令示例说明
LoadLoadBarriersLoad1;LoadLoad;Load2该屏障确保Load1数据的装载先于Load2及其后所有装载指令的的操作
StoreStoreBarriersStore1;StoreStore;Store2该屏障确保Store1立刻刷新数据到内存(使其对其他处理器可见)的操作先于Store2及其后所有存储指令的操作
LoadStoreBarriersLoad1;LoadStore;Store2确保Load1的数据装载先于Store2及其后所有的存储指令刷新数据到内存的操作
StoreLoadBarriersStore1;StoreLoad;Load1该屏障确保Store1立刻刷新数据到内存的操作先于Load2及其后所有装载装载指令的操作.它会使该屏障之前的所有内存访问指令(存储指令和访问指令)完成之后,才执行该屏障之后的内存访问指令
2、原理

内存屏障在Java中的体现

  • 1、volatile读之后,所有变量读写操作都不会重排序到其前面。
  • 2、volatile读之前,所有volatile读写操作都已完成。
  • 3、volatile写之后,volatile变量读写操作都不会重排序到其前面。
  • 4、volatile写之前,所有变量的读写操作都已完成。

根据JMM规则,结合内存屏障的相关分析得出以下结论

  • 1、在每一个volatile写操作前面插入一个StoreStore屏障。这确保了在进行volatile写之前前面的所有普通的写操作都已经刷新到了内存。
  • 2、在每一个volatile写操作后面插入一个StoreLoad屏障。这样可以避免volatile写操作与后面可能存在的volatile读写操作发生重排序。
  • 3、在每一个volatile读操作后面插入一个LoadLoad屏障。这样可以避免volatile读操作和后面普通的读操作进行重排序。
  • 4、在每一个volatile读操作后面插入一个LoadStore屏障。这样可以避免volatile读操作和后面普通的写操作进行重排序。

如下图所示:

3、as-if-serial语义

但是用了volatile关键字,程序的运行速度必然会受到影响,那么除了volatile关键字以外什么时候不会发生重排序呢?这里就要引入as-if-serial语义。

as-if-serial语义的意思是:不管怎么重排序(编译器和处理器为了提供并行度),(单线程)程序的执行结果不能被改变。

如果两个操作访问同一个变量,且这两个操作有一个为写操作,此时这两个操作就存在数据依赖性这里就存在三种情况:1. 读后写;2.写后写;3. 写后读,者三种操作都是存在数据依赖性的。如果重排序会对最终执行结果会存在影响,编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序。

int a=1;
int b=2;
int c =a+b;

as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。比如上面计算的代码,在单线程中,会让人感觉代码是一行一行顺序执行上,实际上a,b两行不存在数据依赖性可能会进行重排序,即a,b不是顺序执行的。as-if-serial语义使程序员不必担心单线程中重排序的问题干扰他们,也无需担心内存可见性问题。

说到底,as-if-serial语义不过是一种最基础的架构定义,可以类比地球上氧气的比例约为21%。

重排序可以分为两类:

会改变程序执行结果的重排序。

不会改变程序执行结果的重排序。

JMM对这两种不同性质的重排序,采取了不同的策略。

  • 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。
  • 对于不会改变程序执行结果的重排序,JMM对编译器和处理器不做要求(JMM允许进行优化重排序)

volatile就是通过对内存语义的封装实现了对volatile关键字读写时的顺序和可见。保证了我们所谓的多线程下的可见性,但是还是没办法保证多线程下修改数据的同步,因为同步除了有序和可见还需要满足原子性。

四、happens-before规则

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

外链图片转存中…(img-gQhmM4bw-1710426714850)]

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

[外链图片转存中…(img-pUH63T39-1710426714850)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/538847.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++ 】stack 和 queue

1. 标准库中的stack stack 的介绍: 1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作 2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其…

LabVIEW电磁阀特性测控系统

LabVIEW电磁阀特性测控系统 电磁阀作为自动化工程中的重要组成部分,其性能直接影响系统的稳定性和可靠性。设计一种基于LabVIEW的电磁阀特性测控系统,通过高精度数据采集和智能化控制技术,实现电磁阀流阻、响应时间及脉冲特性的准确测量和分…

接口的回调

接口是引用型变量 接口回调技术:把实现该接口的类的实例引用赋给接口变 量(存放对象的引用)→接口变量可调用被类重写的接口方法。 ★注意★:接口com无法调用类中非接口方法

MySQL基础---SQL语句2(WHERE、AND、OR、ORDER BY、COUNT)

1. WHERE 子句 1. 语法 WHERE 子句用于限定选择的标准 在 slelece、update、delete 语句中,皆可使用 WHERE 子句来限定选择的标准 -- 查询语句 select 列名称 form 表名称 where 列 运算符 值-- 更新语句 update 列名称 form 列新值 where 列 运算符 值-- 删除语句…

数据库中的数据

从数据库查到的数据,是这种JSON格式,建和值都是双引号包裹 [[{id: 21,name: why,password: e10adc3949ba59abbe56e057f20f883e,createAt: 2024-03-11T06:55:35.000Z,updateAt: 2024-03-11T06:55:35.000Z}],[id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,name VARCH…

3.2 RK3399项目开发实录-初次使用的环境搭建(物联技术666)

通过百度网盘分享的文件:嵌入式物联网单片… 链接:https://pan.baidu.com/s/1Zi9hj41p_dSskPOhIUnu9Q?pwd8qo1 提取码:8qo1 复制这段内容打开「百度网盘APP 即可获取」 1. 用户和密码 1.1. Ubuntu Desktop 系统 Ubuntu Desktop 系统开机启动后,自动登录…

图片或视频充当网页背景+过渡动画

这是目前的主页预览图。上一个版本带有学校logo,根据比赛规则,删掉了学校logo。 图片背景 也就是将图片作为背景。 这个需求产生的场景是:如果直接用img标签显示界面左上角的logo,那么鼠标右键是可以直接选中图片的&#xff0c…

DirectPV的故事

2020 年,MinIO 为基于 Kubernetes 的 MinIO 存储部署实施了直接持久卷 (DirectPV)。DirectPV 类似于 LocalPV,但动态预配。 在这篇文章中,我将介绍创建 DirectPV 的有趣设计决策。但在深入了解设计细节之前&#xff0c…

Logseq电脑端+安卓端同步gitee或github

文章目录 0.初衷1.电脑端1.1 新建仓库1.2 克隆项目,生成秘钥1.3 添加图谱,选择文件目录,我是原本就有笔记,所以会如下所示。1.4 下载脚本文件1.5赋权限 (windows可跳过)1.6 修改脚本命令1.7 logseq设置同步…

es 聚合操作(二)

书接上文,示例数据在上一篇,这里就不展示了 一、Pipeline Aggregation 支持对聚合分析的结果,再次进行聚合分析。 Pipeline 的分析结果会输出到原结果中,根据位置的不同,分为两类: Sibling - 结果和现有…

CentOS无法解析部分网站(域名)

我正在安装helm软件,参考官方文档,要求下载 get-helm-3 这个文件。 但是我执行该条命令后,报错 连接被拒绝: curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 # curl: (7) Fai…

2.MongoDB与关系数据库对比

MongoDB的简单操作与比较 与关系数据库对比 MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库。它们各有各的优点,来看看他们之…