下面的代码,将 setted 变量的 volatile 修饰符去掉则会导致线程 w 死循环在 while(!setted);这一句上。这是由于 w 线程取 setted 值被优化为直接从本 CPU 的 cache-line 中取的缘故。只需要将 setted 变量定义为 volatile 的,对于这个变量的访问便具有 acquire(读) 和 release (写) 语义,于是便观察不到这个现象了。
public class TestAotmic
{
public int value = 0;
public volatile boolean setted = false; //如果是非 volatile 的,则 线程 w 会死循环在 while(!setted) 这一句。
//由于 java 的 volatile 具有读写栅栏的双重语义,也即同时具有 acquire-release语义,故无法
//重现 setted 被赋值,但 value 没有被赋值的现象。实际上这一现象是发生了的,只是没有办法只取出 setted
//的新值,而不影响 r 线程的 value=1 和 setted=true 这两句的重排序。
public TestAotmic()
{
// TODO Auto-generated constructor stub
}
public void doTest()
{
Runnable r = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
value = 1;
setted = true;
}
};
Runnable w = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(!setted);
assert(1 == value);
}
};
Thread tw = new Thread(w);
Thread tr = new Thread(r);
tw.start();
tr.start();
try {
tw.join();
tr.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args)
{
int n = 40;
while(0!= --n)
{
System.out.println(n);
new TestAotmic().doTest();
}
}
}