什么是"未完全构建的对象"?

Joo*_*kka 25 java concurrency publishing this

Goetz的Java Concurrency in Practice,第41页提到了this在构造过程中引用如何逃脱.一个"不要这样做"的例子:

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });
    }
}
Run Code Online (Sandbox Code Playgroud)

this是通过doSomething(e)引用封闭ThisEscape实例的事实"逃避" .可以通过使用静态工厂方法(首先构造普通对象,然后注册侦听器)而不是公共构造函数(完成所有工作)来解决这种情况.这本书继续:

从构造函数中发布对象可以发布未完全构造的对象.这是真实的,即使是公布在构造函数中的最后一条语句.如果this参考在构造期间逃逸,则认为该对象未正确构造.

我不太懂.如果发布是构造函数中的最后一个语句,那么之前没有完成所有构建工作吗?怎么会是this由当时不是有效?显然有一些伏都教在那之后继续,但是什么?

Jon*_*eet 17

就最终字段而言,构造函数的结尾在并发性方面是一个特殊的位置.从Java语言规范的第17.5节:

当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.

最终字段的使用模型很简单.在该对象的构造函数中设置对象的最终字段.在对象的构造函数完成之前,不要在另一个线程可以看到的位置写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.

换句话说,如果检测到另一个线程中的对象,则您的侦听器最终可能会看到具有默认值的最终字段.如果在构造函数完成后发生侦听器注册,则不会发生这种情况.

就发生的事情而言,我怀疑在构造函数的最后有一个隐含的内存障碍,确保所有线程"看到"新数据; 如果没有应用内存屏障,可能会出现问题.


Rob*_*ser 6

当您继承ThisEscape时,会出现另一个问题,并且子类会调用此consructor.EventListener中的隐式this引用将具有未完全构造的对象.