volatile作用与处理器嗅探的简解
先贴一下 volatile 的作用定义
如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的
首先问题就来了,一个共享变量再被volatile修饰过后,怎么被确保所有线程看到的这个变量的值是一致的的呢,也就是说volatile是如何来保证可见性的呢?
在X86处理器下通过工具获取JIT编译器生成的汇编指令来查看对volatile进行写操作时,CPU会做什么事情。
private volatile instance = new Singleton();
转变成汇编代码,如下。
0x01a3de1d: movb $0×0,0×1104800(%esi); 0x01a3de24: lock addl $0×0,(%esp);
有 volatile 变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,通过查IA-32架构软件开发者手册可知,Lock前缀的指令在多核处理器下会引发了两件事情。
1)将当前处理器缓存行的数据写回到系统内存。
2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
第一件事很容易理解,处理器在修改数据后通常都是先写入到缓存中,但是并不会第一时间写回到主内存中。
被 volatile 修饰后,这个变量被操作后会立即被写回到主内存中。(当然整个过程会比较复杂,但是我们只需要从结果上来看和简化理解就OK了。)
那第二件事中这个写回内存的操作是如何使其他CPU里缓存了该内存地址的数据无效的呢?
为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知道何时会写到内存。
如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。
但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。
所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,
当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。
这么看来的话,各处理器之间是通过实现缓存一致性协议来完成第二件事的。
那么“每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了”,这里的处理器是如何嗅探的呢?
我百度了半天也没有单独将如何嗅探的,但是看了一遍博文后(并发研究之CPU缓存一致性协议(MESI)) ,我有了一些自己的理解,当然也只是为了简化复杂流程的简化理解。

以上是目前流行的多级缓存结构简化图,
处理器无论是想要加载数据或者写回数据,都需要通过总线(图中的②bus)来传播,
那么我们也就可以将 “处理器通过嗅探在总线上传播的数据” 这样的操作形象的理解为处理器监听 总线 上的所有修改操作,
当处理器发现自己的缓存中的某个数据在总线上被其他的处理器修改了,那么就将自己缓存中的这个数据的状态变成无效状态,
然后当处理器在处理这个无效状态的数据时,会重新去主内存中加载这个数据,然后在进行相应的操作。
比如:CPU A的cache a 已经缓存了 x,然后 CPU B的cache b 也已经缓存了 x,这时 CPU A要修改 x 的值,
然后先将修改后的数据写回到主内存中,在写回到主内存的同时被CPU B嗅探到了,并且发现这个数据在自己的cache b中也存在,然后CPU B就先将自己cache b中的 x 的状态设置成无效,
当CPU B处理到 x 时,发现 x 的状态是无效的,就只能先去主内存中重新加载 x 的值后再操作。
以上便是个人对于处理器嗅探操作的简化理解,虽然简化理解后的流程顺序和原本的流程顺序有所出入,但是这样简化理解只是为了方便自己理解和记忆。
需要了解具体的整体操作流程的话,可以去看上面提到的那边博文。
volatile作用与处理器嗅探的简解的更多相关文章
- python ConfigParser、shutil、subprocess、ElementTree模块简解
ConfigParser 模块 一.ConfigParser简介ConfigParser 是用来读取配置文件的包.配置文件的格式如下:中括号“[ ]”内包含的为section.section 下面为类 ...
- Jmeter(十八) - 从入门到精通 - JMeter后置处理器 -下篇(详解教程)
1.简介 后置处理器是在发出“取样器请求”之后执行一些操作.取样器用来模拟用户请求,有时候服务器的响应数据在后续请求中需要用到,我们的势必要对这些响应数据进行处理,后置处理器就是来完成这项工作的.例如 ...
- UE4 RHI与Render模块简解
UE4中的RHI指的是Render hardware interface,作用像Ogre里的RenderSystem,针对Dx11,Dx12,Opengl等等平台抽象出相同的接口,我们能方便能使用相同 ...
- zabbix基本监控各指标简解
监控项目及使用模板 监控http和https: Template App HTTP Service Template App HTTPS Service 监控cpu,内存,网络等: Templ ...
- Jmeter(十七) - 从入门到精通 - JMeter后置处理器 -上篇(详解教程)
1.简介 后置处理器是在发出“取样器请求”之后执行一些操作.取样器用来模拟用户请求,有时候服务器的响应数据在后续请求中需要用到,我们的势必要对这些响应数据进行处理,后置处理器就是来完成这项工作的.例如 ...
- Spring ApplicationContext 简解
ApplicationContext是对BeanFactory的扩展,实现BeanFactory的所有功能,并添加了事件传播,国际化,资源文件处理等. configure locations:(C ...
- HTTP协议简解
1.什么是http协议 http协议: 浏览器客户端 与 服务器端 之间数据传输的规范 2.查看http协议的工具 1)使用火狐的firebug插件(右键->查看元素->网络) 2)使用 ...
- python函数基础以及函数参数简解
python中函数分为函数.内置函数Python所有的内置函数 自定义函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. ...
- AC题目简解-数据结构
A - Japan POJ 3067 要两条路有交叉,(x1,y1)(x2,y2)那么需要满足:(x1-x2)*(y1-y2)<0判断出这是求逆序的问题 树状数组求逆序,先通过自定义的比较器实 ...
随机推荐
- luogu P2765 魔术球问题
题目中没有说球的上限是多少,只告诉了柱子,那么我们就应该以柱子为界去增加球,考虑将每两个能组成完全平方数的点连边,就形成了一个DAG(有向无环图),由于是DAG,可以转换为最小覆盖问题,即最多有n条路 ...
- Zero 初识Sciter
在浏览有关Sciter技术前,您需要花点时间浏览以下内容. 您是否需要花时间学习Sciter? 如果您的工作或您想从事的工作与桌面应用开发无关,那么您不需要学习Sciter. 如果您不认同HTML\C ...
- NO23 Linux正则表达式结合三剑客企业级实践--取IP
企业实践: 一.取IP的方法(用三剑客): grep: awk: sed:虽有三种,但是思路是一样的,用到正则有些表达细节不一样而已. 分析: sed***: 课堂试题: |sed -nr 's#^. ...
- SSM+Redis结构框架及概述
1.Spring IoC承担了一个资源管理.整合.即拔即插的功能. 2.Spring AOP可以提供切面管理,特别是数据库事务管理的功能. 3.Spring MVC用于把模型.视图和控制器分层,组成一 ...
- 请求http协议分析- (mysql-thinkphp) (5)
http协议 https://tools.ietf.org/pdf/rfc7231.pdf https://www.w3.org/Protocols/ ======================== ...
- C语言三种整数类型
1,int 是 C 语言的基本整数类型,可以满足我们处理一般数据的需求. C 语言还提供了四个可以修饰 int 的关键字:short.long.signed,以及 unsigned. 利用这四个关键字 ...
- 快速为Eclipse配置PyDev插件
想学习Python,查询网络之后发现PyDev是很好的插件,所以就想为Eclipse配置它.结果在整个配置的过程中出现了各种问题,版本问题,重复问题,反正乱七八糟的,本身安装一次的时间就很长,中间出现 ...
- string.xml中的空格
<string name="userName"> 用 户 名</string>
- 066、Java面向对象之以分步的方式实例化对象
01.代码如下: package TIANPAN; class Book { // 定义一个新的类 String title; // 书的名字 double price; // 书的价格 public ...
- 传入sql语句,执行完提取内容赋值到控件上
class procedure DBTools.FillStrings(ComboBoxEh: TDBComboBoxEh; sql: string; Default: Boolean = False ...