10、谈一谈 volatile

小景哥哥博客

谈一谈 volatile

JMM 为 volatile 定义了一些特殊访问规则,当变量被定义为 volatile 后具备两种特性:

  • 保证变量对所有线程可见

    当一条线程修改了变量值,新值对于其他线程来说是立即可以得知的。volatile 变量在各个线程的工作内存中不存在一致性问题,但 Java 的运算操作符并非原子操作,导致 volatile 变量运算在并发下仍不安全。

  • 禁止指令重排序优化

    使用 volatile 变量进行写操作,汇编指令带有 lock 前缀,相当于一个内存屏障,后面的指令不能重排到内存屏障之前。

    使用 lock 前缀引发两件事:① 将当前处理器缓存行的数据写回系统内存。②使其他处理器的缓存无效。相当于对缓存变量做了一次 store 和 write 操作,让 volatile 变量的修改对其他处理器立即可见。

静态变量 i 执行多线程 i++ 的不安全问题

自增语句由 4 条字节码指令构成的,依次为 getstaticiconst_1iaddputstatic,当 getstatic 把 i 的值取到操作栈顶时,volatile 保证了 i 值在此刻正确,但在执行 iconst_1iadd 时,其他线程可能已经改变了 i 值,操作栈顶的值就变成了过期数据,所以 putstatic 执行后就可能把较小的 i 值同步回了主内存。

适用场景

① 运算结果并不依赖变量的当前值。② 一写多读,只有单一的线程修改变量值。

内存语义

写一个 volatile 变量时,把该线程工作内存中的值刷新到主内存。

读一个 volatile 变量时,把该线程工作内存值置为无效,从主内存读取。

指令重排序特点

第二个操作是 volatile 写,不管第一个操作是什么都不能重排序,确保写之前的操作不会被重排序到写之后。

第一个操作是 volatile 读,不管第二个操作是什么都不能重排序,确保读之后的操作不会被重排序到读之前。

第一个操作是 volatile 写,第二个操作是 volatile 读不能重排序。

JSR-133 增强 volatile 语义的原因

在旧的内存模型中,虽然不允许 volatile 变量间重排序,但允许 volatile 变量与普通变量重排序,可能导致内存不可见问题。JSR-133 严格限制编译器和处理器对 volatile 变量与普通变量的重排序,确保 volatile 的写-读和锁的释放-获取具有相同的内存语义。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

勤奋的凯尔森同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值