下面的代码是否总是打印出data的"正确"值?即使在方法setData中赋值给data和dataReady之间存在其他操作?
或者JVM可以重新排序这些操作,使得RunningThread可以看到dataReady为真,而data仍然为空?如果代码是安全的&正确——volatile字段是如何防止重新排序问题的呢?这是否意味着data本身甚至不需要是volatile的,仅仅使dataReady volatile会导致JVM不缓存实例的其他字段的本地值,其中一个字段是volatile的?
public class RunningThread implements Runnable {
private volatile boolean dataReady = false;
private volatile String data = null;
public void run() {
while (!dataReady){
//Do stuff or wait
}
System.out.println("Value of data '" +data +"' is definitely not null.");
}
//setData called by another thread
public void setData(String dataToSet){
if (dataToSet!=null){
data = dataToSet;
dataReady = true;
}
}
}
一个线程在写入volatile
变量之前执行的所有写操作,在读取前一个线程写入的volatile
变量的值后,将对另一个线程可见。
Java语言规范的措辞是:
如果x和y是同一个线程的动作,并且x在程序顺序上位于y之前,则hb(x, y)。
…
如果 hb (x, y) 和 hb (y, z) ,然后 hb (x, z) 。
…
对
volatile
字段(§8.3.1.4)的写入发生在字段的每次后续读取之前。
所以写入data
发生在之前,同一个线程写入dataReady
, 发生在之前,第二个线程读取dataReady
, 发生在之前,第二个线程读取data
。
换句话说,关于dataReady
变量的内存可见性保证足以保护data
的读取,甚至不需要将data
声明为volatile
。