以下代码将始终打印出数据的“正确”值吗?即使在方法setData中将值分配给data和dataReady之间还有其他操作?

还是可以想象JVM重新排序这些操作,以便RunningThread可以将dataReady视为true,而数据仍为null?

如果该代码是安全且正确的,那么该可变字段如何防止重新排序问题呢?这是否意味着数据本身甚至不需要是易失性的,是否仅使dataReady易失性会导致JVM不为一个易失性实例的其他字段缓存本地值,这有什么用?

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 Language Specification的措辞是:


  如果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

10-04 14:11