Java volatile只保证可见性和禁止指令重排,不保证原子性

volatile关键字作用(变量可见性、禁止指令重排序)

前置知识:内存读取的速度跟不上CPU的速度(会降低CPU执行指令的速度),所以有了CPU高速缓存
也就是说,CPU在从主存读取数据的时候,会从复制一份数据到缓存中,运算结束再把数据刷新到主存中。
MSI协议(缓存一致性):主存有一个共享变量,多个CPU缓存复制一份副本,如果主存中共享变量修改,所以CPU缓存中副本都失效。

1、变量可见性(变量修改->重新到主存读数据)

当一个变量被修饰为volatile的时候,一旦这个变量被写修改了,其他缓存中这个变量的副本就会全部失效,因此得重新到主存中读最新的变量。
这就保证了当一个线程修改了某个值,其他线程就可以立即获取。

2、禁止指令重排序

举个例子,创建对象实例需要三个过程

  1. 在堆中开辟内存
  2. 调用构造器进行初始化
  3. 将对象引用复制给变量
    但是由于指令重排并不保证2一定在3前面执行,但1一定是最先执行的的(2,3对1有依赖关系)
    如果此时3先执行,然后访问成员,由于还没执行2的初始化,会导致异常。
    表示加入内存屏障,告诉CPU和编译器,这个屏障前后的指令不能进行重排序。

volatile不保证原子性

有个变量 int cnt=0;

  1. 线程A和线程B同时读到缓存中都是cnt=0
  2. 线程A先执行 cnt=cnt+1,得到cnt=1存放到寄存器A中,然后线程A就阻塞了。由于寄存器A中的值还未刷新到主存中,所以主存中的数据还是cnt=0,线程B中cnt=0缓存还不会失效。
  3. 此时线程B马上执行cnt=cnt+1,并马上刷回到主存中,此时触发MSI(缓存一致性原则),导致线程A中缓存值失效。
  4. 线程A中值cnt失效,重新到主存中获取cnt=1,然后再把寄存器(MSI协议并不会映像到寄存器)中的cnt=1刷新到主存中,这就导致了线程安全问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值