volatile作用及实现原理

volatile作用

保证不同线程之间对共享变量操作时的可见性

在多线程的环境下,如果某个线程首次读取共享变量,则首先到主内存中获取该变量,然后存入工作内存中,以后只需要在工作内存中读取该变量即可。同样如果对该变量执行了修改操作,则先将新值写入工作内存中,然后再刷新至主内存中。但是什么时候最新的至会被刷新至主内存是不确定的,所以会导致线程间的共享变量不可见。

当一个变量被volatile关键字修饰时,对于共享资源的读操作会直接在主内存中进行,当然也会缓存到工作内存中,当其他线程对该共享资源进行了修改,则会导致当前线程在工作内存中的共享资源失效(反映到硬件上就是CPU的L1或L2的Cache Line失效,MESI协议[1]),所以必须从主内存中再次获取。对于共享资源的写操作是要先修改工作内存,但是修改结束后会立刻将其刷新到主内存中。

禁止对指令进行重排序操作

在Java的内存模型中,允许编译器和处理器对指令进行重排序(目的是提高CPU效率),在单线程的情况下,重排序不会引起什么问题,但是在多线程的情况下,重排序会影响到程序的正确运行。

volatile关键字修饰的变量在C++中会加"lock"指令,"lock"实际上内存屏障[2]

volatile在JVM中的实现:

​      StoreStoreBarrier ┃ volatile写操作 ┃ StoreLoadBarrier

​      LoadLoadBarrier  ┃ volatile读操作 ┃ LoadStoreBarrier

[1] MESI协议

MESI协议保证了每一个缓存中使用的共享变量副本都是一致的,它的大体思想是,当CPU在操作Cache中的数据时,如果发现该变量是一个共享变量,也就是说在其他的CPU中也存在一个副本,那么进行如下的操作:

  1. 读取操作,不作任何处理,只是将Cache中的数据读取到寄存器。

  2. 写入操作,发出信号通知其他CPU将该变量的Cache Line置为无效状态,其他CPU在进行该变量读取的时候不得不到主内存中再次获取。

[2] 内存屏障
cpu硬件级别的内存屏障

sfence: 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。

lfence:在lfence指令前的读操作当必须在lfence指令后的读操作前完成。

mfence:mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。(全能屏障)

jvm规范中的内存屏障

LoadLoad屏障:读读屏障(load1|loadload|load2)(load1必须在load2之前运行读取完以下三个类似)

StoreStore屏障:写写屏障(store1|storestore|store2)

LoadStore屏障:读写屏障(load|loadstore|store)

StoreLoad平常:写读屏障(store|storeload|load)(全能屏障)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值