Java JVM';当链接构造器时,内隐记忆障碍的表现如何?

Java JVM';当链接构造器时,内隐记忆障碍的表现如何?,java,constructor,jvm,compiler-optimization,memory-barriers,Java,Constructor,Jvm,Compiler Optimization,Memory Barriers,关于我的问题,我还有第二个问题。正如Jon Skeet指出的,在构造函数的末尾有一个隐式内存障碍,它确保所有线程都可以看到final字段。但是如果一个构造函数调用另一个构造函数会怎样呢;在他们每个人的末尾都有这样的记忆障碍吗,还是只在第一次被调用的那个人的末尾有这样的记忆障碍?也就是说,当“错误”的解决方案是: public class ThisEscape { public ThisEscape(EventSource source) { source.register

关于我的问题,我还有第二个问题。正如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正在这样做

当一个对象的构造函数完成时,它被认为是完全初始化的

这也适用于链式构造函数


如果必须在构造函数中注册,请将侦听器定义为静态内部类。这是安全的。

在建议编译器内联私有构造函数(我没有想到优化)的注释之后,编辑,代码可能是不安全的。不安全的多线程代码中最糟糕的部分是它似乎可以工作,所以最好完全避免它。如果你想玩不同的技巧(你确实想要避免某个原因),考虑添加一个包装器来保证内部实现对象中数据的一致性,并在外部对象中登记。

我猜它会很脆弱,但还行。编译器无法知道是否只从其他构造函数中调用内部构造函数,因此它必须确保只调用内部构造函数的代码的结果是正确的,因此它使用的任何机制(内存屏障?)都必须在那里

我猜编译器会在每个构造函数的末尾添加内存屏障。问题仍然存在:在完全构造之前,您正在将
这个
引用传递给其他代码(可能是其他线程),这很糟糕,但是如果剩下的唯一“构造”是注册侦听器,那么对象状态将一如既往地稳定

该解决方案是脆弱的,因为在某一天,您或其他程序员可能需要向对象添加另一个成员,并且可能忘记链式构造函数是一种并发技巧,并且可能决定在公共构造函数中初始化字段,这样做会在您的应用程序中增加难以检测的潜在数据竞争,所以我会尽量避免这种构造


顺便说一句:猜测的安全性可能是错误的。我不知道编译器有多复杂/聪明,也不知道内存障碍(或类似的东西)是否可以尝试优化掉。。。由于构造函数是私有的,编译器有足够的信息知道它只从其他构造函数调用,并且这些信息足以确定内部构造函数中不需要同步机制…

c-tor中的转义对象引用可以发布未完全构造的对象。即使发布是构造函数中的最后一条语句,也是如此


即使执行了c-tor内联,您的SafeListener在并发环境中的行为也可能不正常(我认为并非如此——请考虑通过访问私有c-tor使用反射创建对象)。

您的第二个版本不正确,因为它允许从构造过程中转义“this”引用。使用“this”转义将使赋予final字段安全性的初始化安全保证失效


为了解决隐式问题,构建结束时的障碍只发生在对象构建的最后。读者提供的关于内联的直觉是有用的;从Java内存模型的角度来看,方法边界不存在

我认为这是安全的,因为java内存模型声明:

设o为对象,c为o的构造函数,其中 写入字段f。在o的最后一个字段f上发生冻结操作 当c退出时,通常或突然退出。请注意,如果 构造函数调用另一个构造函数,被调用的构造函数 设置最终字段时,最终字段的冻结在 调用的构造函数的结尾


“编译器无法知道是否只从其他构造函数中调用内部构造函数,因此必须确保只调用内部构造函数的代码的结果是正确的。”-非常好的想法,谢谢!除非,如果编译器检测到内部构造函数是从另一个构造函数调用的,然后不是实际调用内部构造函数,而是将其内联到公共构造函数中,该怎么办。。。但是希望没有这样的优化:-)你已经做到了:我没有想到构造函数的内联,但是这是一个非常简单的优化,大多数当前的编译器实际上可以执行。所以答案应该是:不要这样做。你确定编译器没有检测到私有构造函数正在从另一个构造函数调用,然后它将内联到调用(公共)构造函数中,而不是实际被调用,以节省一个内存障碍吗