volatile 的作用

volatile 的主要作用有三点: - 保证变量的内存可见性 ,有序性(禁止指令重排序),不保证原子性。

可见性

简单解释:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

不可见的原理:以多核CPU为例(两核),CPU的速度比内存要快,CPU都会有自己的高速缓存区,当内核运行的线程执行一段代码时,首先要将这段代码的指令集填充到高速缓存,若是变量被CPU修改后,会将修改的值刷新到高速缓存,然后在刷新到内存中。此时若另一个内核使用这个变量,这个变量依然是旧的。
volatile关键字解决的问题就是:当一个线程写入该值后,另一个线程读取的必定是新值。
volatile关键字原理:加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障
它有三个功能

  • 确保指令重排序时不会把其后面的指令重排到内存屏障之前的位置,也不会把前面的指令排到内存屏障后面,即在执行到内存屏障这句指令时,前面的操作已经全部完成;
  • 将当前处理器缓存行的数据立即写回系统内存(由volatile先行发生原则保证);
  • 这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。写回操作时要经过总线传播数据,而每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效状态,当处理器要对这个值进行修改的时候,会强制重新从系统内存里把数据读到处理器缓存(也是由volatile先行发生原则保证);

不保证原子性

原子性定义:某个线程正在作某个业务时,不可以加塞或者被分割,需要整体性

为什么不能保证原子性呢?

在我们的程序中,即使加了volatile也不能保证原子性,有一种粗暴的解决办法,就是加我们的synchronized同步锁,但是这种锁太重了。简单画个图演示为什么不能保证原子性。


在程序中的i++操作,是不能保证原子性的.由于多个线程疯抢,线程1刚写进主存还没来得及通知其他线程,线程2就写入造成丢失写。

那原子性和可见性不是就冲突了吗?不冲突
因为volatile 的可见性只能对应l原子性, a=1是原子性,而a++实际上是a=a+1 是非原子性的,所以会导致你说的情况,这时候就要引入同步,强制将a++转化为原子性。

怎么解决原子性
那就是大名鼎鼎的JUC包下的AtomicInteger,写一小段程序说明:

AtomicInteger atomicInteger=new AtomicInteger();
atomicInteger.getAndIncrement();//获取在增加

有序性

单线程环境中指令重排序可以保证最终执行结果和代码顺序执行一致。

处理器进行指令重排必须考虑指令之间的数据依赖性(先有鸡才可以有蛋)
但是在多线程环境中程序交替执行,由于编译器指令重排序存在,两个线程中使用的变量能否保持一致是不确定的。所以volatile禁止指令重排序

既然CPU有了MESI协议可以保证cache的一致性,那么为什么还需要volatile这个关键词来保证可见性(内存屏障)?或者是只有加了volatile的变量在多核cpu执行的时候才会触发缓存一致性协议?

两个解释结论:

多核情况下,所有的cpu操作都会涉及缓存一致性的校验,只不过该协议是弱一致性,不能保证一个线程修改变量后,其他线程立马可见,也就是说虽然其他CPU状态已经置为无效,但是当前CPU可能将数据修改之后又去做其他事情,没有来得及将修改后的变量刷新回主存,而如果此时其他CPU需要使用该变量,则又会从主存中读取到旧的值。而volatile则可以保证可见性,即立即刷新回主存,修改操作和写回操作必须是一个原子操作;
正常情况下,系统操作并不会进行缓存一致性的校验,只有变量被volatile修饰了,该变量所在的缓存行才被赋予缓存一致性的校验功能。

我们再来看一下另一位大N的解释:

首先,volatile是java语言层面给出的保证,MSEI协议是多核cpu保证cache一致性(后面会细说这个一致性)的一种方法,中间隔的还很远,我们可以先来做几个假设:

回到远古时候 那个时候cpu只有单核,或者是多核但是保证sequence
consistency[1],当然也无所谓有没有MESI协议了。那这个时候,我们需要java语言层面的volatile的支持吗?当然是需要的,因为在语言层面编译器和虚拟机为了做性能优化,可能会存在指令重排的可能,而volatile给我们提供了一种能力,我们可以告诉编译器,什么可以重排,什么不可以。
那好
假设更进一步,假设java语言层面不会对指令做任何的优化重排,那在多核cpu的场景下,我们还需要volatile关键字吗?答案仍然是需要的。因为
MESI只是保证了多核cpu的独占cache之间的一致性,但是cpu的并不是直接把数据写入L1 cache的,中间还可能有store
buffer。有些arm和power架构的cpu还可能有load buffer或者invalid
queue等等。因此,有MESI协议远远不够。 再接着 让我们再做一个更大胆的假设。假设cpu中这类store buffer/invalid
queue等等都不存在了,cpu是数据是直接写入cache的,读取也是直接从cache读的,那还需要volatile关键字吗?你猜的没错,还需要的。原因就在这个“一致性”上。consistency和coherence都可以被翻译为一致性,但是MSEI协议这里保证的仅仅coherence而不是consistency。那consistency和cohence有什么区别呢?下面取自wiki[2]的一段话:
Coherence deals with maintaining a global order in which writes to a
single location or single variable are seen by all processors.
Consistency deals with the ordering of operations to multiple
locations with respect to all processors.

因此,MESI协议最多只是保证了对于一个变量,在多个核上的读写顺序,对于多个变量而言是没有任何保证的。很遗憾,还是需要volatile~~

好的,到了现在这步,我们再来做最后一个假设,假设cpu写cache都是按照指令顺序fifo写的,那现在可以抛弃volatile了吧?你觉得呢?我都写到标题4了,那肯定不行啊!因为对于arm和power这个weak
consistency[3]的架构的cpu来说,它们只会保证指令之间有比如控制依赖,数据依赖,地址依赖等等依赖关系的指令间提交的先后顺序,而对于完全没有依赖关系的指令,比如x=1;y=2,它们是不会保证执行提交的顺序的,除非你使用了volatile,java把volatile编译成arm和power能够识别的barrier指令,这个时候才是按顺序的。

volatile和MESI协议之间的关系
刚接触MESI协议时容易产生误解,以为是volatile关键字触发MESI机制来保证变量的可见性,实际没有这一层因果关系。下面简单总结下我对两者关系的一些理解:

1.MESI保障了多核场景的缓存一致性,是一套固有机制,无论是否声明volatile,只要变量在CPU缓存中就能通过这个协议保障可见性。但反过来,如果没有MESI协议,即使声明了volatile也无法保证变量的可见性;

2.既然有了MESI协议,为什么还要volatile?这是因为MESI本身存在一些性能问题(例如修改一个存在于其他CPU缓存的数据时,需要等待其他CPU的invalidate ack),因此额外引入了store buffer和invalidate queue等存储结构来优化性能。当数据仅存在于L1/L2 cache中时,MESI足可以保证volatile的内存语义,但如果数据存在于store buffer或者invalidate queue中时,仅靠MESI无法保证数据的一致性,这时需要引入内存屏障

原子操作

Lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

MESI协议保证:当修改数据时,CPU工作内存中数据存回主内存时,被cpu总线嗅探机制监听到,cpu1中的值失效,重新从主内存读取。

有volatile修饰的共享变量进行写操作的时候多出一条带lock前缀的指令

volatile可见性MESI协议volatile相关推荐

  1. 【原理/Java并发】从volatile到MESI协议

    文章目录 1 前言 2 有序性 2.1 编译器层面的内存屏障 2.2 CPU层面的内存屏障 3 可见性 3.1 MESI协议 3.2 Store Buffer 和 Invalid Queue 3.3 ...

  2. java顶级内功心法(1) —— 可见性、MESI、volatile精讲 (8000字由浅入深)

    可见性.volatile精讲 前言 一.voaltile是什么意思? 二.可见性问题的探索 1.多线程环境下的可见性问题 2.共享变量为什么"不可见" 3.RAM的更新时机 3.1 ...

  3. java 类型不可视_jvm高级特性(5)(1)(原子性,可见性,有序性,volatile,概述)

    简介: 阿姆达尔定律(Amdahl):该定律通过系统中并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 摩尔定律(Moore):该定律用于描述处理器晶体管数量与运行效率间的发展关系. 当价 ...

  4. MESI与Volatile(二)

    MESI缓存一致性协议 定义: 多核CPU的情况下有多个一级缓存,一致性协议MESI保证缓存内部数据的一致,不让系统数据混乱. 缓存行(Cache line):缓存存储数据的单元 缓存行状态: 状态 ...

  5. 从汇编看volatile与MESI的关系

    JMM 1.来谈谈JMM 2.volatile和synchronized关键字 2.1volatile是如何保证可见性的?? 2.2 volatile是如何保证有序性的呢?? 哪些情况不能重排序?? ...

  6. 关于volatile、MESI、内存屏障、#Lock

    最近又看了下Disruptor,里面提到了内存屏障,突然想到了指令重排.还有可见性,感觉里面关系有点乱,就翻了下,因此就写了这篇文章 带着几个问题: 1.volatile,是怎么可见性的问题(CPU缓 ...

  7. java 可见性_Java并发编程-volatile可见性详解

    前言 要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸.最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍. 有什么 ...

  8. java volatile 用法_java关键字volatile用法详解

    volatile关键字想必大家都不陌生,在java 5之前有着挺大的争议,在java 5之后才逐渐被大家接受,同时作为java的关键字之一,其作用自然是不可小觑的,要知道它是java.util.con ...

  9. mesi协议怎么实现_volatile的底层实现原理

    volatile关键字有两个作用 保证被volatile修饰的共享变量(vlatile int a=1)对所有线程总数可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总是可以 ...

最新文章

  1. Django1.11 扩展User属性增加头像上传功能
  2. python fastapi_Python|介绍一下我的新伙伴fastapi(一)
  3. Expected an Objective directive after @ 的解决办法
  4. python基础之os.system函数执行命令行语句
  5. 动手学pytorch之通俗易懂何为卷积-深度AI科普团队
  6. 全国快递物流查询公司mysql数据库语句
  7. 【Python123】汽车迷
  8. Python导入模块的3种方式(超级详细)
  9. 【逗老师的无线电】宝峰神机刷OpenGD77摇身变为DMR大热点
  10. FineBI教程之入门例子
  11. python设置颜色_使用python改变颜色的色调
  12. 滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(8月8日~8月14日)...
  13. 用青龙跑渤海宣传员(收益很稳定)
  14. 2019年最新的一波手机APP分享,每一个都是大家的菜哦!
  15. SQL Server XML 数据类型
  16. html请求的跨域问题
  17. linux 实现监听热插拔事件
  18. 基于scrapy框架的关于58同城招聘网站信息的爬取
  19. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java宜居家居用品网jte0e
  20. Arduino与Proteus仿真实例-温控风扇仿真

热门文章

  1. 实现div元素和文字水平及垂直居中的方法(超简单,适应各种场合)
  2. 技术分享 | mysql Federated 引擎最佳实战
  3. 腾讯云服务器网页存在哪里找,腾讯云 AMD服务器入口在哪里?
  4. 解析java中方法的重载和重写之间的区别与联系
  5. Android7.0更换系统默认输入法
  6. SQL语句中的case when,decode的用法
  7. 水桶实验:牛顿与马赫
  8. linux 轻系统下载,7款值得尝试的轻量级Linux操作系统。
  9. c# word 增加段落_Word排版费时又费力?用上这款插件,再冗杂的内容也能轻松排好...
  10. 【Hexo搭建GitPage博客系列】02.环境搭建