我有这个类,我在其中缓存实例并在使用它们时克隆它们(数据是可变的)。
我想知道我是否可以面临重新排序的问题。
我已经看过这个答案和 JLS,但我仍然没有信心。
public class DataWrapper {
private static final ConcurrentMap<String, DataWrapper> map = new ConcurrentHashMap<>();
private Data data;
private String name;
public static DataWrapper getInstance(String name) {
DataWrapper instance = map.get(name);
if (instance == null) {
instance = new DataWrapper(name);
}
return instance.cloneInstance();
}
private DataWrapper(String name) {
this.name = name;
this.data = loadData(name); // A heavy method
map.put(name, this); // I know
}
private DataWrapper cloneInstance() {
return new DataWrapper(this);
}
private DataWrapper(DataWrapper that) {
this.name = that.name;
this.data = that.data.cloneInstance();
}
}
我的想法:运行时可以重新排序构造函数中的语句并在初始化对象之前发布当前DataWrapper
实例(放入映射中) 。data
第二个线程DataWrapper
从映射读取实例并看到空data
字段(或部分构造)。
这可能吗?如果是,是否只是由于引用转义?
如果不是,您能否解释一下如何用更简单的术语来推理发生前的一致性?
如果我这样做会怎样:
public class DataWrapper {
...
public static DataWrapper getInstance(String name) {
DataWrapper instance = map.get(name);
if (instance == null) {
instance = new DataWrapper(name);
map.put(name, instance);
}
return instance.cloneInstance();
}
private DataWrapper(String name) {
this.name = name;
this.data = loadData(name); // A heavy method
}
...
}
它仍然容易出现同样的问题吗?
请注意,如果多个线程尝试同时创建并放置相同值的实例,我不介意是否创建了一个或两个额外的实例。
编辑:
如果 name 和 data 字段是 final 或 volatile 怎么办?
public class DataWrapper {
private static final ConcurrentMap<String, DataWrapper> map = new ConcurrentHashMap<>();
private final Data data;
private final String name;
...
private DataWrapper(String name) {
this.name = name;
this.data = loadData(name); // A heavy method
map.put(name, this); // I know
}
...
}
还是不安全吗?据我了解,构造函数初始化安全保证仅适用于初始化期间引用未转义的情况。我正在寻找证实这一点的官方消息来源。