文章目录

  • 回答volatile关键字
  • 1、可见性
  • 2、禁止指令重排
  • 3、不保证原子性

回答volatile关键字

volatile关键字的三个特性:可见性、禁止指令重排、不保证原子性

1、可见性

此处的核心是理解MESI协议,基于volatile关键字展开,然后延申到MESI协议,并且完成MESI协议相关的一些计算机组成的概念的补充

①现象:保证了可见性,在进行线程交互中过程中,被该关键字修饰的变量会具有可见性,能够保证变量在线程间的信息是同步的

②字节码:被该关键字修饰的变量在被编译为字节码的时候,会添加了一个ACC_VOLATILE关键字

③理论基础:MESI协议,可见性的本质特性是基于计算机的缓存一致性协议。MESI分别对应已修改、独占、共享、无效。之所以该协议能够保证变量在不同线程间具有可见性,就是得益于该协议。

在理解理解MESI协议之前需要了解的一下计算机组成相关的概念:

  1. 总线事务:从我们的请求开始,到我们的请求结束,这一整个过程称之为总线事务。
  2. 总线仲裁:在我们的总线事务请求过程中,如果出现,多个处理器同时访问同一片内存空间,那么就一定要对我们的多个请求做出决策,到底使用执行哪一个请求。这个选择的过程叫做总线裁决。
  3. 总线锁定:当总线裁决后,只会有一个请求去执行对应的操作,此时就会出现一个LOCK#信号量(类比理解为Java中的synchronized,添加一个锁),出现了该信号量,表示出现了总线锁定
  4. 缓存锁定:与总线锁定类似,只是锁定的粒度不同,此时锁定的是缓存块。(缓存锁定和总线锁定的区别就好比InnoDB存储引擎下的行锁和表锁)
  5. 总线窥探:在缓存锁定的基础上,通过缓存锁定自己独特的方式去实现数据同步问题,这个独特的方式就是MESI协议,这个MESI协议的基础背景理论则是总线窥探机制。当我们将数据写入一个缓存数据行的时候,其他总线是能够感知到数据变化,继而做出相对应的调整。这就是第一点中所说的为什么volatile能够保证可见性,他背后的原理就是MESI协议,自然也就离不开这里的总线窥探机制

总结:总线事务总是存在,所以需要总线裁决来决定到底是哪一个进行,常规情况下我们直接使用总线锁定,这样就能够保证数据的安全。但是由于人们希望能够提高处理效率,所以又基于总线窥探机制,提出了一个缓存锁定的理论。将原来的锁定内存块变成了粒度更小的缓存块。

MESI协议(详细版请参考浅谈volatile与计算机缓存一致性协议(MESI)之间的联系):

情景前提为,两个线程共同访问num=1这个变量,MESI协议在这个过程中的体现

  1. 线程1将主存中的num = 1加载到线程1私有的缓存空间中。由于num被volatile关键字修饰,所以会执行对应的一致性协议(添加对应的LOCK前缀指令)。此时线程1的缓存状态为独占独占(E);
  2. 如果此时线程2也加载了num变量,那么线程1和线程2的各自的缓存空间对应的缓存状态就会变成共享(S);
  3. 此时线程1将缓存中的数据加载到CPU,在ALU(算数逻辑单元)的配合下完成对应的逻辑运算;
  4. 线程1的CPU完成逻辑运算后,将数据返回给缓存。此时总线发现了数据发生变化,就会修改对应缓存空间的缓存状态,将当前线程的缓存状态修改为已修改(M),其他线程中对应的缓存块状态修改为无效(I)。于此同时,还需要将线程1中的缓存变量num,立即刷新回主存;
  5. 那么当线程2想加载自己缓存空间中对应的变量时,就无法命中(原因就是第四步),所以又重新去主存中加载,此时线程2加载到的num就变成了最新的数据;
  6. 最后线程2在将数据加载到CPU完成自己的逻辑运算,最终返回给主存。

总结:简单来说就是,最开始的人拿到了数据就是E,如果第二个人也拿到了数据就变了两个S,如果一个人修改了数据,那么状态就会变成M,然后数据放到主存,与此同时第二个人的就变成I。既然是无效的I,那么第二个人在使用数据的时候就只能自己再去主存中获取,此时获取到的数据,就成个最新的一笔数据。

2、禁止指令重排

程序在执行的时候,内部会自动优化掉一些不影响结果的逻辑,这个时候就会涉及代码顺序的调整。那么添加了volatile关键字后,就能够禁止代码结构的优化。编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器进行重排序。编译器选择了比较保守的JMM内存屏障策略,这样就能保证在任何处理器平台上,任何程序中都能够得到正确的volatie内存语义。这个策略分成:

LoadStore:在读后面再插入

StoreStore:在写前插入

StoreLoad:在写后插入

LoadLoad:在写读后插入

当然,如果我们只是要解决我们的问题,那么我们添加一个volatile关键字就可以了。如果局部变量也需要使用volatile,那么我们可以手动的添加内存屏障。我们可以使用Java自带的Unsafe工具类,调用对应的方法完成该操作。不过该方法只能通过反射调用。

3、不保证原子性

对任意单个volatile变量的读/写具有原子性,但类似于i++这种复合操作不具有原子性

  • 常见的例子就是使用两个线程对一个变量进行i++操作,最后的结果值不等于循环的次数(我个人不是很能理解为什么会把这一个点当作一个特性来描述,因为i++不具有原子性是客观存在的,即便是你不添加volatile也是没有办保证原子性的,既然i++本身就有问题,那为什又要和volatile关键字扯上关系呢?)
  • 还有一个比较常见的例子,就是单例模式的双重检测锁,我们需要对instance变量添加volatile关键字,因为很有可能在赋值的位置,由于创建对象的过程不是原子性的(类比理解i++),加上这个过程可能会出现指令重排,所以添加对应的关键字就能够很好的避免创建单例对象过程出现的线程安全问题。

要是面试官再问我volatile,我就这么答相关推荐

  1. 面试官再问我如何保证 RocketMQ 不丢失消息,这回我笑了!

    0x00. 消息的发送流程 一条消息从生产到被消费,将会经历三个阶段: 生产阶段,Producer 新建消息,然后通过网络将消息投递给 MQ Broker 存储阶段,消息将会存储在 Broker 端磁 ...

  2. 面试官再问你 HashMap 底层原理,就把这篇文章甩给他看

    来自:烟雨星空 前言 HashMap 源码和底层原理在现在面试中是必问的.因此,我们非常有必要搞清楚它的底层实现和思想,才能在面试中对答如流,跟面试官大战三百回合.文章较长,介绍了很多原理性的问题,希 ...

  3. 查询已有链表的hashmap_面试官再问你 HashMap 底层原理,就把这篇文章甩给他看...

    前言 HashMap 源码和底层原理在现在面试中是必问的.因此,我们非常有必要搞清楚它的底层实现和思想,才能在面试中对答如流,跟面试官大战三百回合.文章较长,介绍了很多原理性的问题,希望对你有所帮助~ ...

  4. qt 如何 指针 自动 释放内存_要是面试官再问你智能指针的问题,就拿这篇文章“盘他”!!!...

    前一段时间,有不少朋友问我关于智能指针的问题,并且反映经常会在面试中被面试官问到,所以今天小豆君就来讲讲我对智能指针的理解,希望能对大家有所帮助 既然讲智能指针,我们就先来看看它为什么会出现. 1 传 ...

  5. 程序员过关斩将--面试官再问你Http请求过程,怼回去!

    Http介绍 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.所有的WWW文件都必须遵守这个标准.设计HTTP最初的目的是为了提 ...

  6. 99%的面试官都会问的一个问题,这样答就能轻松过关

    上周面试一个姑娘,本来聊得挺好. 她曾在一家单位做过两年文职,开朗.落落大方,我看见离职原因一栏写着"个人原因",就问,能说说你为什么从上家单位离开么? 她立刻吧嗒吧嗒说了一大堆. ...

  7. 大多数面试官都会问的一个问题,这样答就能轻松过关

    点击上方"程序人生",选择"置顶公众号" 第一时间关注程序猿(媛)身边的故事 作者 焱公子 如需转载,请联系原作者. 上周面试一个姑娘,本来聊得挺好. 她曾在一 ...

  8. 面试官再问高并发,求你把这篇发给他!

    高并发,几乎是每个程序员都想拥有的经验.原因很简单:随着流量变大,会遇到各种各样的技术问题,比如接口响应超时.CPU load升高.GC频繁.死锁.大数据量存储等等,这些问题能推动我们在技术深度上不断 ...

  9. websphere mq 查看队列中是否有数据_如果面试官再问你消息队列,就把这篇甩给他!...

    ★★★建议星标我们★★★ 公众号改版后文章乱序推荐,希望你可以点击上方"Java进阶架构师",点击右上角,将我们设为★"星标"!这样才不会错过每日进阶架构文章呀 ...

  10. 面试官再问线程池,你这样谈谈线程的回收,好感会倍增!

    来源 | https://www.cnblogs.com/kingsleylam/p/11241625.html 最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流 ...

最新文章

  1. LDAPit's usage
  2. MySQL - mysqldump多种方式实现数据迁移
  3. 删除sql下注册服务器
  4. Exynos4412 文件系统制作(三)—— 文件系统移植
  5. iOS 创建推送证书
  6. 在美国公司架构中,LLC、LLP 和 Corporation 的区别何在?
  7. 2021-10-13快速查询DHL物流信息,并筛选出提前签收单号
  8. 测度论与概率论基础(程士宏)学习笔记(一)
  9. 怎么拷贝计算机桌面,电脑怎么拷贝软件
  10. 我的 Serverless 实战 — Serverless 腾讯云文字识别(OCR)详细部署过程
  11. 怎么用c语言编写单片机流水灯程序,单片机编程入门:单片机流水灯程序
  12. 【给初学者,大佬见笑】100%成功UEFI安装双硬盘单系统Ubuntu最合理分区安装指南+ubuntu20.04安装
  13. 用photoShop简单提取他人的签名
  14. JT/T 808-2013 道路运输车辆卫星定位系统北斗兼容车载终端通讯协议技术规范
  15. springboot 根据身份证号计算性别和年龄
  16. python使用turtle库、绘制一个八边形_【Python】turtle八边形绘制
  17. html clear 清楚li,CSS清楚浮动clear:both
  18. 谢绝枯燥快乐编程,四大游戏编程网站,让你边玩游戏,边学Python
  19. html怎样把字幕贴着色块走,手把手教你用HTML做音画贴
  20. 无穷小微积分,入门三道坎儿

热门文章

  1. AK的故事之英语学习篇(mistake)
  2. 还没有合并再请求pull_代码被revert,你pull了master代码没了咋找回来
  3. 多层陶瓷电容器用处_具有综合优异电卡性能的无铅多层陶瓷电容器研究新进展...
  4. centos7安装python2.6_centos7.2下yum和python重装问题及解决方法
  5. linux读取.data文件,Android获取文件夹路径 /data/data/
  6. js计算字典的个数_第11天:Python 字典
  7. java 没有junit包,~项目导入时报错:程序包org.junit不存在
  8. Power BI能否做帕累托分析
  9. python3 linux
  10. 裴(pei)蜀定理 知识点