我在项目中经常看到这种类型的代码,在这些项目中,应用程序需要一个全局数据持有者,因此他们使用任何线程都可以访问的静态单例。

public class GlobalData {

    // Data-related code. This could be anything; I've used a simple String.
    //
    private String someData;
    public String getData() { return someData; }
    public void setData(String data) { someData = data; }

    // Singleton code
    //
    private static GlobalData INSTANCE;
    private GlobalData() {}
    public synchronized GlobalData getInstance() {
       if (INSTANCE == null) INSTANCE = new GlobalData();
       return INSTANCE;
    }
}

我希望很容易看到正在发生的事情。一个人可以随时在任何线程上调用GlobalData.getInstance().getData()。如果两个线程使用不同的值调用setData(),即使您不能保证哪个赢了,我也不用担心。

但是,这里我不关心线程安全。我担心的是内存可见性。只要Java中存在内存障碍,高速缓存的内存就会在相应的线程之间进行同步。通过同步,访问 volatile 变量等时,会发生内存屏障。

想象以下情况按时间顺序发生:
// Thread 1
GlobalData d = GlobalData.getInstance();
d.setData("one");

// Thread 2
GlobalData d = GlobalData.getInstance();
d.setData("two");

// Thread 1
String value = d.getData();

线程1中value的最后一个值是否仍然可以是"one"吗?原因是,线程2在调用d.setData("two")之后再也没有调用任何同步方法,所以再也没有内存障碍了吗?请注意,这种情况下的内存屏障会在每次调用getInstance()时发生,因为它是同步的。

最佳答案



是的。 Java内存模型基于事前(hb)关系。在您的情况下,由于synced关键字,您只有getInstance退出发生-在后续的getInstance输入之前。

因此,如果我们以您的示例为例(假设线程交织按该顺序进行):

// Thread 1
GlobalData d = GlobalData.getInstance(); //S1
d.setData("one");

// Thread 2
GlobalData d = GlobalData.getInstance(); //S2
d.setData("two");

// Thread 1
String value = d.getData();

您有S1 hb S2。如果在S2之后从Thread2调用d.getData(),您将看到“一个”。但是,不能保证对d的最后读取是“2”。

关于java - 在Java中以静态单例访问的变量的内存可见性是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18197501/

10-11 22:36
查看更多