gpt4 book ai didi

java - 在 Java 的静态单例中访问的变量的内存可见性是什么?

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:51:49 25 4
gpt4 key购买 nike

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

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() 时都会发生内存障碍,因为它是同步的。

最佳答案

你完全正确。

不能保证在一个 Thread 中的写入在另一个线程中可见。

要提供此保证,您需要使用 volatile 关键字:

private volatile String someData;

顺便说一下,您可以利用 Java 类加载器为您的单例提供线程安全的惰性初始化,如文档中所述 here .这避免了 synchronized 关键字,因此为您节省了一些锁定。

值得注意的是,目前公认的最佳实践是使用 enum 在 Java 中存储单例数据。

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

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com