volatile禁止重排序详解
首先说明本文并不是讲解volatile不保证原子性、如何保证可见性xxxx,还不懂的请参考
让你彻底理解volatile
并发关键字volatile(重排序和内存屏障)
本文针对以下两个问题解答
1. 重排序规则中,volatile读写跟普通读写有什么关系,为什么要限制它们
2. volatile读操作的内存屏障的LoadLoad屏障到底是在读前还是读后
直接进入主题
重排序规则中,volatile读写跟普通读写有什么关系,为什么要限制它们
volatile变量的读写不能重排序很好理解,可是这跟普通读写有什么关系呢?普通读写是怎么影响到结果的?
首先明确volatile读/写的内存语义:
- 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。(注意:是所有的共享变量,不光是volatile变量)
- 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存 (注意:这里也是所有的共享变量)
即:无论是volatile读还是volatile写,都会刷新本地内存的所有共享变量,而不单是volatile变量
保证了在读volatile变量之前,内存中所有的共享变量都是最新的,也就是之前执行的的任意线程的写操作都对本次的读操作可见;
同样,本次写操作都对之后任意线程执行的读操作可见。
可能有读者觉得这样没什么必要,那我们来举例看看不这么限制的话会有什么后果
volatile int a = 0;
int b = 1;public void A (){b = 2; // 1 普通写a = 1; // 2 volatile写
}public void B() {int c = 0;if (a == 1) // 3 volatile读c = b; // 4 普通写
}
假如volatile不影响重排序,那么由于代码1和代码2两处没有数据依赖性,所以二者是可以重排序的。
我们假设代码2在代码1之前被执行,此时由于a是volatile变量,所以将a = 1, b = 1刷新进入主内存;
如果这时候方法A所在的线程cpu时间片用完了,轮到了方法B在另一个线程中执行,由于a是volatile变量所以代码3处执行的时候会将b = 1, a = 1从主内存中读出,此时代码4再执行的话c会变为1,而不是预想的2(因为按照我们书写的顺序来看,a=1发生在b=2之后)
发生这种错误的原因在于:volatile变量写操作与在其之前的代码发生了重排序,使得刷新内存的时机提早了,可能会漏掉我们写在volatile变量赋值操作之前的那些共享变量的修改。
所以这就引出了volatile变量对指令重排序的第一个影响:
- 第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。确保volatile写操作刷新内存里共享变量的值时,程序员希望发生的变动都能够正确的刷新到内存中
而这例子也对应:
第一个操作为普通读/写,第二个操作为volatile写,也验证了确实是不能重排序,因为是可能会产生影响的。
PS:笔者建议此处应该按第二个操作去记忆
同理,再举个例子解释,第一个操作为volatile读,第二个操作为普通读/写 不能重排原因。
首先要明确happens-before中的猜测执行机制:
当存在控制依赖时,编译器和处理器会采取猜测执行机制来提高并行度
int a = 1;
boolean flag = true ;
if(flag){ //代码5a * = 2; //代码6
}
代码5和6不存在数据依赖,可能会重排,处理器和编译器会先将代码6的执行结果放在缓冲区,等代码5判断为真时之后,将缓冲区的结果直接赋值给a,即实质上对操作5和6做了重排序
那么继续用上述例子,稍作修改
volatile int a = 1;
int b = 1;public void A (){a = 2; //1 volatile写
}public void B() {int c = 0;if (a == 1) // 3 volatile读c = b; // 4 普通写
}
现在换成先执行方法B。
基于上述的猜测执行机制,程序会把b=1先写到缓冲区,
如果这时候方法B所在的线程cpu时间片用完了,轮到了方法A在另一个线程中执行,把a赋值为2,然后返回方法B所在的线程,可是此时不满足判断条件,所以c不会被赋值为1,即c的值还是为0,而这显然是不对的。
同理,引出第二条规则:
- 第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。确保volatile读操作读取内存里的最新值是程序员希望读到的、操作的值
到此,规则中跟普通读写相关的“NO”已经解释完了
而volatile变量的读写间不能重排序就不举例了。
volatile读操作的内存屏障的LoadLoad屏障到底是在读前还是读后
在说volatile读之前,我们根据规则表,先来推volatile写的内存屏障
- 第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。所以在写操作前插入StoreStore屏障
- 第一个操作是volatile写时,第二个操作的volatile读/写操作都不能重排序。所以在写操作后插入StoreLoad屏障
好的,符合,那我们现在来推volatile读操作的内存屏障
第一个操作是volatile读操作时,不管第二个操作是什么,都不能重排序。而第二个操作则包括了读、写(不管是普通读写还是volatile读写)。
- 所以对于后续的读操作,则应在volatile读后插入LoadLoad屏障
- 对于后续的写操作,则应在volatile读后插入LoadStore屏障
其实到这里,就已经可以了,但我们可以反推否定是读前插入:因为第二个操作为volatile读时,对于普通读/写,是没有限制“NO”的,如图
正推跟反推,都证明了,LoadLoad是在读后插入的
本文到此完,吐槽一下笔者查了好多资料,居然没有相关文章,因此整理出来,希望能给各位一点帮助。
有误欢迎指出。
参考文章:
Java并发学习笔记 – Java中的Lock、volatile、同步关键字
volatile的指令重排序理解
指令重排序
volatile禁止重排序详解相关推荐
- 利用结构体数组实现重排序(详解)
一:要求 输入乱序的 成绩单 包括 姓名和成绩,成绩按照递增顺序 输出,如果遇见成绩相同的 按名字的字典序输出. 示例: 输入 5 w 12 a 12 v 7 c 3 e 9 输出: c 3 v 7 ...
- ConcurrentProgramming:volatile/构造方法溢出/禁止重排序
ConcurrentProgramming:volatile/构造方法溢出/禁止重排序 关键词 内存可见性原理(直接操作主存) 禁止指令重排原理(内存屏障,最终目的:直接操作主内存) 对volatil ...
- pandas dataframe中的列进行重新排序、倒排、正排、自定义排序详解及实践
pandas dataframe中的列进行重新排序,pandas dataframe列重排.倒排.正排.自定义排序详解及实践 实施数据构建: import pandas as pd import nu ...
- 希尔排序基础java代码_java 算法之希尔排序详解及实现代码
摘要:这篇Java开发技术栏目下的"java 算法之希尔排序详解及实现代码",介绍的技术点是"希尔排序详解.实现代码.希尔排序.Java.实现.代码",希望对大 ...
- Linux 中使用 sort 指令分组排序详解
Linux 中使用 sort 指令分组排序详解 sort 中进行分组排序主要用到的选项为 -k,此文,我们着重于该选项的使用方式,用到的其它选项不做解释,有兴趣的同学可以查看帮助文档 1. 数据准备 ...
- (*(volatile unsigned int *))详解
(*(volatile un 从日剧看日本单身狗现状 登录注册 阿拉丁神丢的博客 http://blog.sina.com.cn/ddlovetechnology [订阅][手机订阅] 首页博文目录图 ...
- 地理坐标系、大地坐标系与地图投影与重投影详解
地理坐标系.大地坐标系与地图投影与重投影详解 基本概念 首先简单介绍一下地理坐标系.大地坐标系以及地图投影的概念: 地理坐标系:为球面坐标. 参考平面地是椭球面,坐标单位:经纬度: 投影坐标系:为平面 ...
- c语言排序常用问题,【更新中】【排序详解】解决排序问题(以C语言为例)
[更新中][排序详解]解决排序问题(以C语言为例) [更新中][排序详解]解决排序问题(以C语言为例) 文章目录 排序的相关概念 简单排序 一.插入排序: (一)插入排序基本思想 (二)插入排序基本操 ...
- 八大排序 详解(下)——指向函数的指针 的使用
<八大排序 详解(上)> <八大排序 详解(中)> 紧接前两篇博文,我们这篇博文所要讲解的是我们前两篇博文编写的所有函数的使用.生成随机数组的函数的讲解以及一种及其凶悍的调用方 ...
最新文章
- .NET 3.5 中WCF客户端代理性能改进以及最佳实践
- 什么叫n+1次select查询问题?
- ASP.NET Core 对Controller进行单元测试
- from mysql partition select_爬虫(九十九)mysql详解二
- 7个良心到难以置信的自媒体免费网站推荐
- wordpress邮件地址混淆 你没权限访问整个邮件地址造成的死链接
- BZOJ 3097: Hash Killer I【构造题,思维题】
- Linux运维工程师必备工具合集
- 进阶项目(6)LCD12864液晶屏幕设计讲解
- 功能安全标准-ISO26262-1---使用范围和主要内容
- 1.3【展讯平台】Android 驱动(Kernel)、系统(framework) 定制,调试日志
- mac 命令行 解压7z文件_命令行压缩解压7z
- python 圆形检测_基于opencv-python的圆形检测
- World Cup Betting(PAT)
- 【工作效率提升】Search Everything 下载安装
- python中间值怎么获得_使用python 3的中间值
- 安卓开发之使用第三方的聚合数据API,QQ测吉凶案、身份证号码查询。
- 【Cocos笔记1】摇杆,移动,转向
- php 条码校验位,Code128条形码如何计算其宽度?如何得出其校验位?
- 单调队列-原理详解(deque实现)
热门文章
- 学习各种计算机知识网站推荐
- 使用Markdown语言在博客的文章中添加视频
- 电子学会青少年等级考试C语言(二级)
- 微信小程序优惠券样式
- python爬虫_爬取京东商品写入Excel表
- C语言连续自动自加怎么表示,为什么不建议在C语言中连续使用自增自减运算符...
- ubuntu有怪怪的声音一直跟读屏幕解决方法
- 关于c3p0报错:An attempt by a client to checkout a Connection has timed out.
- 慧荣SM3267AB主控U盘量产的工具
- 自学Python问题记录2:解决画风玫瑰图出现报错No artists with labels found to put in legend.