链接构造函数时,JVM的隐式内存屏障如何表现?
问题内容:
关于我先前关于不完整构造的对象的问题,我有第二个问题。正如Jon
Skeet所指出的那样,在构造函数的末尾有一个隐式的内存屏障,以确保final
字段对所有线程可见。但是,如果一个构造函数调用另一个构造函数,该怎么办?它们每个的末尾是否存在这样的存储障碍,或者仅在首先被调用的那个末尾存在?也就是说,当“错误”的解决方案是:
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
正确的是工厂方法版本:
public class SafeListener {
private final EventListener listener;
private SafeListener() {
listener = new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
}
}
public static SafeListener newInstance(EventSource source) {
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}
以下工作也可以吗?
public class MyListener {
private final EventListener listener;
private MyListener() {
listener = new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
}
}
public MyListener(EventSource source) {
this();
source.register(listener);
}
}
更新: 一个基本问题是,可以this()
保证实际上 调用
上面的私有构造函数(在这种情况下,原本打算使用的障碍将是安全的,并且一切都将是安全的),或者私有构造函数有可能作为一个 内联对象 被 内联
到公共对象中优化以节省一个内存屏障(在这种情况下,直到公共构造函数的末尾才会有屏障)?
this()
定义的规则正好在某处吗?如果没有,那么我认为我们必须假定允许内联链接的构造函数,并且可能某些JVM甚至javac
s都在这样做。
问题答案:
我认为这是安全的,因为Java内存模型指出:
令 o 为对象, c 为 o 的构造函数,其中写入最终字段 f 。当 c 正常或突然退出时,会对 o的 最终字段 f
进行冻结操作。请注意,如果一个构造函数调用了另一个构造函数,并且被调用的构造函数设置了一个final字段,则最终字段的冻结发生在被调用的构造函数的末尾。