Java Volatile关键字
1. Volatile
1.1 基本概念
内存可见性
内存可见性,指的是线程之间的可见性,当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值。
重排序
为优化程序性能,对原有的指令执行顺序进行优化重新排序。重排序可能发生在多个阶段,比如编译重排序、CPU重排序。
1.2 volatile的内存语义
- 保证变量的内存可见性
- 禁止volatile变量与普通变量重排序
内存可见性
public class VolatileExample {
int a = 0;
volatile boolean flag = false;
public void write(){
a = 1; // step 1
flag = true; // step 2
}
public void reader(){
if (flag){ // step 3
System.out.println(a);
}
}
}
所谓内存可见性,指的是当⼀个线程对 volatile 修饰的变量进⾏写操作(⽐如step 2)时,JMM会立即把该线程对应的本地内存中的共享变量的值刷新到主内存;当⼀个线程对 volatile 修饰的变量进⾏读操作(⽐如step 3)时,JMM会把⽴即该线程对应的本地内存置为⽆效
,从主内存中读取共享变量的值。
禁止重排序
JSR-133专家组决定增强volatile的内存语义:严格限制编译器和处理器对volatile变量与普通变量的重排序。
编译器还好说,JVM是怎么还能限制处理器的重排序的呢?它是通过内存屏障
来实现的。
什么是内存屏障?硬件层面,内存屏障分两种:读屏障(Load Barrier)和写屏障 (Store Barrier)。内存屏障有两个作⽤:
- 阻⽌屏障两侧的指令重排序;
- 强制把写缓冲区/⾼速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效。
编译器在⽣成字节码时,会在指令序列中插⼊内存屏障来禁⽌特定类型的处理器重排序。编译器选择了⼀个⽐较保守的JMM内存屏障插⼊策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:
- 在每个volatile写操作前插⼊⼀个StoreStore屏障;
- 在每个volatile写操作后插⼊⼀个StoreLoad屏障;
- 在每个volatile读操作后插⼊⼀个LoadLoad屏障;
- 在每个volatile读操作后再插⼊⼀个LoadStore屏障。
版权声明:本文为qq_44209336原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。