volatile写入是否确保在一个线程中的任何写入(非易失性/易失性写入)发生在其他线程可见之前?
以下给定代码是否始终生成90,80
作为输出?
public class MyClass
{
private boolean flag = false;
private volatile int volatileInt = 0;
private int nonVolatileInt = 0;
public void initVariables()
{
nonVolatileInt = 90; // non-volatile write
volatileInt = 80; // volatile write
flag = true; // non-volatile write
}
public void readVariables()
{
while (flag == false)
{}
System.out.println(nonVolatileInt + ","+ volatileInt);
}
public static void main(String st[])
{
final MyClass myClass = new MyClass();
Thread writer = new Thread( new Runnable()
{
public void run()
{
myClass.initVariables();
}
});
Thread reader = new Thread ( new Runnable()
{
public void run()
{
myClass.readVariables();
}
});
reader.start();writer.start();
}
}
我关心的是initVariables()方法。是不是JVM可以自由地按以下方式重新排序代码块?:
flag = true;
nonVolatileInt = 90 ;
volatileInt = 80;
因此,我们将读者线程的输出视为:0,0
或者,它们可以通过以下方式重新排序:
nonVolatieInt = 90;
flag = true;
volatileInt = 80;
因此,我们将读者线程的输出作为:90,0
答案 0 :(得分:5)
易失性写入确保在写入后不会出现已执行的写入。但是为了确保您看到这一点,您需要先执行易失性读取。
因此,我们将读者线程的输出作为:
90,0
正确。但是,如果您正确执行了阅读,则无法获得0, 80
0, 0 - ok
90, 0 - ok
90, 80 - ok
0, 80 - breaks happens before.
但是,您的读取不能确保在行为之前发生,因为它不会先执行易失性读取。
System.out.println(nonVolatileInt + ","+ volatileInt);
这首先读取非易失性字段,因此您可以看到旧版本的非易失性字段和新版本的volatile字段。
注意:实际上,你不太可能看到问题。这是因为缓存一次使整个缓存行无效,如果这些字段在同一个64字节的块中,则不应该看到不一致。
更有可能出现问题的是这个循环。
while (flag == false)
{}
问题是; JIT可以看到你的线程nevers写入flag
,因此它可以内联值。即它永远不需要读取价值。这可能导致无限循环。
http://vanillajava.blogspot.co.uk/2012/01/demonstrating-when-volatile-is-required.html