先贴一下 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作用与处理器嗅探的简解的更多相关文章

  1. python ConfigParser、shutil、subprocess、ElementTree模块简解

    ConfigParser 模块 一.ConfigParser简介ConfigParser 是用来读取配置文件的包.配置文件的格式如下:中括号“[ ]”内包含的为section.section 下面为类 ...

  2. Jmeter(十八) - 从入门到精通 - JMeter后置处理器 -下篇(详解教程)

    1.简介 后置处理器是在发出“取样器请求”之后执行一些操作.取样器用来模拟用户请求,有时候服务器的响应数据在后续请求中需要用到,我们的势必要对这些响应数据进行处理,后置处理器就是来完成这项工作的.例如 ...

  3. UE4 RHI与Render模块简解

    UE4中的RHI指的是Render hardware interface,作用像Ogre里的RenderSystem,针对Dx11,Dx12,Opengl等等平台抽象出相同的接口,我们能方便能使用相同 ...

  4. zabbix基本监控各指标简解

    监控项目及使用模板 监控http和https: Template App HTTP Service     Template App HTTPS Service 监控cpu,内存,网络等: Templ ...

  5. Jmeter(十七) - 从入门到精通 - JMeter后置处理器 -上篇(详解教程)

    1.简介 后置处理器是在发出“取样器请求”之后执行一些操作.取样器用来模拟用户请求,有时候服务器的响应数据在后续请求中需要用到,我们的势必要对这些响应数据进行处理,后置处理器就是来完成这项工作的.例如 ...

  6. Spring ApplicationContext 简解

    ApplicationContext是对BeanFactory的扩展,实现BeanFactory的所有功能,并添加了事件传播,国际化,资源文件处理等.   configure locations:(C ...

  7. HTTP协议简解

    1.什么是http协议 http协议: 浏览器客户端 与  服务器端 之间数据传输的规范 2.查看http协议的工具 1)使用火狐的firebug插件(右键->查看元素->网络) 2)使用 ...

  8. python函数基础以及函数参数简解

    python中函数分为函数.内置函数Python所有的内置函数 自定义函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. ...

  9. AC题目简解-数据结构

    A - Japan  POJ 3067 要两条路有交叉,(x1,y1)(x2,y2)那么需要满足:(x1-x2)*(y1-y2)<0判断出这是求逆序的问题 树状数组求逆序,先通过自定义的比较器实 ...

随机推荐

  1. 如何在Java中测试类是否是线程安全的

    通过优锐课的java核心笔记中,我们可以看到关于如何在java中测试类是否线程安全的一些知识点汇总,分享给大家学习参考. 线程安全性测试与典型的单线程测试不同.为了测试一个方法是否是线程安全的,我们需 ...

  2. php-计算2个时间之差

    //$startdate是开始时间,$enddate是结束时间 <?php $startdate="2011-3-15 11:50:00"; $enddate="2 ...

  3. Redis散列表类型

    散列类型(hash)的键值也是一种字典结构,其存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他的数据类型. 一个散列类型键可以包含至多2^32 -1个字段. 命令 赋值 HSE ...

  4. Mysql8.0免安装包配置方法

    1. 官网下载mysql_8.0.12免安装包,解压到你存放的地方: https://www.jb51.net/softs/609101.html 2. 配置环境变量(把bin的文件夹弄进系统path ...

  5. gitlab实现webhook触发jenkins 自动,构建,测试,push webhook构子 总结

    最新一直在学习 工作 + 学习 去掉 90%   所以blog  一直没更 真是很不好!  exsi ceph gitlab jenkins harbor k8s  docker-compose ap ...

  6. 今日份学习: Spring中使用AOP并实现redis缓存?

    笔记 在Spring中如何使用AOP? Spring是如何切换JDK动态代理和CGLIB的? spring.aop.proxy-target-class=true (在下方第二个链接中,原生doc中提 ...

  7. C++字符集和词法记号以及符号常量

    C++字符集 大小写的英文字母:A~Z,a~z 数字符号:0~9 特殊字符:!#%*()等 关键字:C++预定义的单词 标识符:程序员声明的单词,它命名程序正文中的一些实体 文字:在程序中直接使用符号 ...

  8. Metasploit学习笔记——环境配置

    <Metasploit渗透测试魔鬼训练营>书56页开始配置网络环境,一共五台机器,攻击机换成了自己更常用的kali,配置方法和back track相同. kali(攻击机) 10.10.1 ...

  9. 向量容器vector操作

    1.向量容器vector 1.1 vector说明 进行vector操作前应添加头文件#include<vector>: vector是向量类型,可以容纳许多类型的数据,因此也被称为容器: ...

  10. 时间戳,秒级,毫秒级转换DateTime格式

    解决了本地时间和格林尼治时间差问题 function DateTimeToTp(ConvDate: TDateTime): time_t;var zi: TTimeZoneInformation;be ...