要了解这个关键字的作用,我们需要知道两个概念
- 可见性
Java编程语言中允许线程访问共享变量。为了确保共享变量能够被准确和一致的更新,线程应该确保通过排它锁单独获得这个变量。 - 指令重排
在编程执行过程中为了性能考虑,编译器和CPU可能会对指令重新排序。对于并发多线程场景下,指令重排会产生不确定的执行效果。
所以volatile针对可见性
- volatile关键字修饰的共享变量可以提供这种可见性范围,也叫 读写可见
- 被volatile修饰的共享变量在转换成汇编语言时,会加上一个以lock为前缀的指令,当CPU发现这个指令时,立即将当前内核高速缓存行的数据回写到内存,同时使在其他内核里缓存了该内存地址的数据无效
- 在早期的CPU中,是通过在总线加LOCK锁的方式实现的,但这种方式开销较大。所以Intel开发了缓存一致性协议,也就是MESI协议,来解决缓存一致性
- 好处 volatile是一种非锁机制,这种机制可以避免锁机制引起的线程上下文切换和调度问题。所以,volatile的执行成本比synchronized更低
- 不足 volatile关键字只能保证可见性,不能保证原子性的操作
针对指令重排
public volatile boolean sign;
descriptor: Z
flags: ACC_PUBLIC, ACC_VOLATILE
从字节码层面,添加ACC_VOLATILE,在汇编指令的打印会有lock addl$0x0,(%rsp)s
从JVM层面,JMM提供了8个Happen-Before规则来约束数据之间竞争,4个内存屏障(LL LS SL SS)和As-if-serial
从硬件层面,sfence、Ifence、mfence