Java中的Volatile关键字

问题根源

在多线程运行场景中,每个线程可能运行与 不同的CPU中,每个线程的读写都是操作自己CPU所在的高速缓存,但操作的数据存储在堆中,作为共享变量,每个线程操作的实际上是自己本地线程的副本,在并发 编程中,可能就会存在缓存不一致的问题

早期的CPU是通过总线加Lock#锁的方式解决缓存不一致问题,对总线加Lock#锁只保证只能有一个CPU使用这个变量的内存,这样会导致其他CPU无法访问内存,效率低,现在主要是通过缓存一致性协议来解决缓存一致性问题

Volatile特征

  • Volatile保证了不同线程对该线程操作的内存可见性
  • 禁止编译器进行指令性重排
  • volatile变量是一种轻量同步机制,访问volatile不会执行加锁操作
  • volatile无法同事保证内存可见性和 原子性,因此更建议使用加锁(同步)机制

Volatile解决内存可见性问题

之前我们分析过 ,多线程的读写并不会操作主内存(共享内存),而是操作线程的本地副本,这也是导致线程间数据不可见的本质,Volatile主要是从这个角度解决问题

  1. 修改volatile修饰的变量,CPU会强制将修改后的值刷新到主内存中
  2. 修改volatile修饰的变量,会导致其他线程 的本地内存的对应该变量值失效,该变量值的获取需要再重新从共享内存中读取

如何防止编译器指令性重排

编译器语义重排的后果

编译器遵守as-if-serial 语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。

class ReorderExample {
int a = 0;
boolean flag = false;

public void writer() {
    a = 1;                   //1
    flag = true;             //2
}

Public void reader() {
    if (flag) {                //3
        int i =  a * a;        //4
        ……
    }
}
}  

如图所示,操作1和操作2做了重排序。程序执行时,线程A首先写标记变量 flag,随后线程 B 读这个变量。由于条件判断为真,线程 B 将读取变量a。此时,变量 a 还根本没有被线程 A 写入,在这里多线程程序的语义被重排序破坏了!在这里插入图片描述

volatile关键字保证有序性

* volatile关键字关键字实际上给CPU添加了一个内存屏障,它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成

volatile使用要点

  1. 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。

  2. 该变量没有包含在具有其他变量的不变式中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值