Java多线程之volatile保证可见性原理

一、volatile的作用

  • 可见性:当一个变量被volatile修饰时,将保证此变量对所有线程的可见性。

  • 有序性:使用volatile修饰的变量能禁止指令重排优化。

二、禁止指令重排

在底层源码中,volatile是通过给变量赋值之后,加一个"内存屏障"来实现禁止指令重排。

2.1 内存屏障是什么?

内存屏障就是一类同步屏障指令,是CPU或者编译器在对内存随机访问的操作中的一个同步点,只有在此点之前的所有读写操作都执行后才可以执行此点之后的操作

2.2 JMM层面的“内存屏障”

先了解两个指令。

  • load(加载):将主存中读取到的值指向工作内存中变量副本。
  • store(存储):将工作内存中变量副本的值传递到主内存。
屏障名称示例说明
LoadLoad Barriersload1;LoadLoad;load2;确保load1读操作先于load2
StoreStore Barriersstore1; StoreStore; store2确保store1将数据写回主存的操作先于store2
LoadStore Barriersload1; LoadStore; store2确保load1读操作先于store2将数据写回主存的操作
StoreLoad Barriersstore1; LoadStore; load2确保store1将数据写回主存的操作先于load2的操作,它会使该屏障之前所有内存访问指令完成之后,才能执行该屏障之后的内存访问指令。

StoreLoad Barriers同时具备其他三个屏障的效果,开销比较大,被目前多数cpu所支持。

三、源码剖析
  1. Java代码
public class Demo2 {

    volatile static int num = 0;

    public static void main(String[] args) {

        num = 8;
    }
}

  1. 字节码文件

用javap -v 命令查看生成的class文件。

字节码层面num属性的访问标志是ACC_STATIC, ACC_VOLATILE

putstatic:给静态变量赋值的操作。

在这里插入图片描述

  1. JVM源码

我们直接看openjdk8底层的putstatic赋值源码。openjdk8源码git地址

/hotspot/src/share/vm/interpreter路径下的bytecodeInterpreter.cpp文件

3.1、cache->is_volatile():根据num属性访问标识判断是否是volatile;

bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; }

如果是volatile根据不同类型赋值,执行OrderAccess::storeload();,这个方法的名字比较眼熟,storeload不就是内存屏障的一种嘛,见名思意,这个代码中应该是和内存屏障有关的代码。

bytecodeInterpreter.cpp:
在这里插入图片描述

3.2、OrderAccess::storeload()

这里有一个需要注意的地方,因为不同的操作系统和不同的CPU有不同的实现,这里记得要选择对应的文件查看。

咱们选择Linux_x86版本的查看。

src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp:
在这里插入图片描述
从上图代码中,可以看出最终会执行OrderAccess::fence(),里面会做一些判断,但最终会执行一个lock前缀指令

lock指令是一个汇编指令,在执行它时,会使CPU释放一个LOCK# 信号,它会锁住对应变量的内存地址,这样就确保在多处理器或多线程的情况下互斥使用这个内存地址,相当于当前线程给这个变量赋值时,其他线程都不能读取。当指令执行完毕,这个锁定动作也就会消失。

总结

底层在给volatile变量赋值时,会发送一个lock指令,这个指令会锁住特定的内存地址,并将线程中该变量缓存行的数据立即写回主存,并触发总线嗅探机制。读取和修改都会经过总线,总线嗅探机制会嗅探总线上共享变量的改变,如果CPU发现自己线程中缓存的该共享变量被修改了,会将该缓存行置为无效状态,然后从主存中再次加载该共享变量的值。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吖土豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值