volatile关键字作用解析:为何添加后1,0输出情况不复存在?
编译器重排序与Volatile的内存语义解析
原始代码(无Volatile)
class OrderingTest { var x = 0 var y = 0 fun test() { thread { x = 1 y = 1 } thread { val a = y val b = x println("$a, $b") } } }
原始代码的四种可能输出
1,0 0,1 0,0 1,1
修改后代码(给y添加@Volatile)
class OrderingTest { var x = 0 @Volatile var y = 0 fun test() { thread { x = 1 y = 1 } thread { val a = y val b = x println("$a, $b") } } }
为什么添加@Volatile后1,0的输出不可能?
要搞懂这个问题,得抓住@Volatile的两个核心内存语义:
- 禁止指令重排序:针对写volatile变量的操作,JVM会保证在它之前的所有普通变量写操作,不能被重排到它之后执行。
- 内存可见性:写volatile变量时,会把之前所有变量的修改同步到主内存;读volatile变量时,会强制从主内存读取,并且能看到所有在写这个volatile变量之前的修改。
具体到这段代码:
- 对于第一个线程的
x=1和y=1:
因为y是volatile变量,JVM会强制x=1的执行顺序在y=1之前,绝对不会出现先写y=1再写x=1的情况。而且当y=1被写入主内存时,x=1的修改也会同步到主内存。 - 对于第二个线程的
val a = y和val b = x:
当第二个线程读到a=1(也就是y=1)时,根据volatile的内存可见性规则,它必然能看到第一个线程在写y=1之前完成的所有修改——也就是x=1。所以此时读取x得到的b一定是1,不可能出现a=1但b=0的情况。
所以添加@Volatile后,1,0的输出就彻底被排除了。
内容的提问来源于stack exchange,提问作者cheems
相关产品推荐
相关产品推荐

