前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java的volatile

Java的volatile

作者头像
用户3467126
发布2019-07-12 15:24:37
3370
发布2019-07-12 15:24:37
举报
文章被收录于专栏:爱编码爱编码爱编码
简介

为了保证线程安全语义,一般的建议是在操作共享变量时加锁,比方说在用synchronized关键字修饰的方法内读写共享变量。

但是synchronized开销较大,有没有更轻量更优雅的解决方案呢?

volatile是轻量级的synchronized,在正确使用的前提下,它可以达到与synchronized一样的线程安全的语义,而且不会带来线程切换的开销。

volatile的作用是什么?

volatile保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它在某些情况下比synchronized的开销更小。

原理

实现机制

volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令(具体的大家可以使用一些工具去看一下,这里我就只把结果说出来)。

那么Lock前缀的指令在多核处理器下会发现什么事情了? 主要有这两个方面的影响:

1.将当前处理器缓存行的数据写回系统内存; 2.这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效

为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。

所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。

因此,经过分析我们可以得出如下结论:

1.Lock前缀的指令会引起处理器缓存写回内存; 2.一个处理器的缓存回写到内存会导致其他处理器的缓存失效; 3.当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值。

这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。

1.volatile变量被某一线程所修改

2.其他线程访问该volatile变量,它本地内存的版本已经过期,所以会从主内存中取

如何阻止重排序

为了实现volatile内存语义时,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎是不可能的,为此,JMM采取了保守策略:

在每个volatile写操作前面插入一个StoreStore屏障 在每个volatile写操作后面插入一个StoreLoad屏障 在每个volatile读操作后面插入一个LoadLoad屏障 在每个volatile读操作后面插入一个LoadStore屏障

需要注意的是:volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障

StoreStore屏障:禁止上面的普通写和下面的volatile写重排序; StoreLoad屏障:防止上面的volatile写与下面可能有的volatile读/写重排序 LoadLoad屏障:禁止下面所有的普通读操作和上面的volatile读重排序 LoadStore屏障:禁止下面所有的普通写操作和上面的volatile读重排序

注意点

讲解了volatile关键字的作用和原理,但对volatile的使用过程中很容易出现的一个问题是:错把volatile变量当做原子变量。 出现这种误解的原因,主要是volatile关键字使变量的读、写具有了“原子性”。然而这种原子性仅限于变量(包括引用)的读和写,无法涵盖变量上的任何操作,即:

基本类型的自增(如count++)等操作不是原子的。可用Atomxx原子类 对象的任何非原子成员调用(包括成员变量和成员方法)不是原子的。

参考文章

https://juejin.im/post/5ae9b41b518825670b33e6c4 https://www.cnblogs.com/stevenczp/p/7214250.html

最后

如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。 关注公众号【爱编码】,小编会一直更新文章的哦。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原理
    • 实现机制
      • 如何阻止重排序
      • 注意点
      • 参考文章
      • 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档