参考资料:Java多线程发展简史
volatile在Java中用于保证可见性,但这么讲还是过于抽象。发现这篇文章里关于volatile的例子非常好,于是抄过来了
public class Volatile {
public static void main(String[] args) {
final Volatile volObj = new Volatile();
Thread t2 = new Thread() {
public void run() {
while (true) {
volObj.check();
}
}
};
t2.start();
Thread t1 = new Thread() {
public void run() {
while (true) {
volObj.swap();
}
}
};
t1.start();
}
boolean boolValue;// use volatile to print "WTF!"
public void check() {
if (boolValue == !boolValue)
System.out.println("WTF!");
}
public void swap() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolValue = !boolValue;
}
}
代码中存在两个线程,一个线程通过一个死循环不断在变换boolValue的取值;另一个线程每100毫秒执行“boolValue==!boolValue”,这行代码会取两次boolValue,可以想象的是,有一定概率会出现这两次取boolValue结果不一致的情况,那么这个时候就会打印“WTF!”。
但是,上面的情况是对boolValue使用volatile修饰保证其可见性的情况下出现的,如果不对boolValue使用volatile修饰,运行时就一次不会出现(起码在我的电脑上)打印“WTF!”的情形,换句话说,这反而是不太正常的,我无法猜测JVM做了什么操作,基本上唯一可以确定的是,没有用volatile修饰的时候,boolValue在获取的时候,并不能总取到最真实的值。
以上是原文中的分析。其实这里的分析还是有些小问题的,作者的意思是,使用 volatile
修饰 boolValue
,会打印 “WTF”;不使用 volatile
修饰 boolValue
,不会出现 “WTF”,认为这种现象不正常。
其实恰恰相反,这里的输出完全符合我们对 volatile 的预期,是正常现象。分析如下:
首先,check()
方法中 boolValue == !boolValue
这一句不是原子操作。简单地讲,不妨认为它至少包含两次读 boolValue 值的过程。
另外,看下图
如果有 volatile
修饰 boolValue
,则各个线程中的 var1, var2 均应与主存中的保持一致。在这个例子中就是,t1,t2线程中的boolValue均与主存中的一致。考虑以下执行过程(这里只是一种可能的上下文切换过程)
- t1 从主存中读 boolValue 值, 假定这时 boolValue 为 true
- t2 修改了主存中的 boolValue 值, 这时 boolValue 变成 false
- t1 再次从主存中读 boolValue 值,这时取到的值是 false
- t1 执行
==
操作进行判断, 显然 true == !false
所以会打印 “WTF”
反过来,如果没有 volatile
修饰 boolValue
,则上述过程很可能变成
- t1 从自己的内存副本中读 boolValue 值,假定这里 boolValue 为 true
- t2 修改线程内存副本中的 boolValue 值,这里的 boolValue 是变成什么值并不重要 (注意,这两个 boolValue 是不同的副本,所以互不影响)
- t1 再次从自己的内存副本中读 boolValue,这时取到的值是 true
- t1 执行
==
操作进行判断,显然 true != !true
所以这次不会打印 “WTF”