我在项目中经常看到这种类型的代码,其中应用程序需要一个全局数据持有者,因此他们使用任何线程都可以访问的静态单例。
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()
调用时都会发生内存屏障,因为它是同步的。