什么是“不完整的对象”?

Goetz的“ Java并发实践 ”第41页提到了this参考在构build过程中如何逃脱。 一个“不要这样做”的例子:

 public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener( new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } } 

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

从其构造函数中发布一个对象可以发布一个不完整构造的对象。 即使发布是构造函数中的最后一个语句,也是如此。 如果this参考文件在施工过程中逃脱了,那么该对象被认为没有正确的构造。

我不太明白 如果出版物是构造函数中的最后一个声明,那么在此之前并没有完成所有的构build工作? 那么这到底是不是有效? 显然有一些巫术之后,但什么?

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

构造函数完成时,对象被认为是完全初始化的。 一个线程,只能看到一个对象的引用后,该对象已被完全初始化,保证看到该对象的最终字段正确初始化的值。

final字段的使用模型很简单。 在该对象的构造函数中设置对象的最终字段。 在对象的构造函数完成之前,不要在另一个线程可以看到它的位置写入正在构造的对象的引用。 如果是这样的话,那么当另一个线程看到这个对象的时候,这个线程总会看到这个对象的最终字段的正确构造版本。 它还会看到最终字段所引用的任何对象或数组的版本,这些字段至less与最终字段一样是最新的。

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

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

另一个问题出现在子类ThisEscape,并且子类调用此Consructor。 EventListener中隐含的这个引用会有一个不完整的构造对象。

registerListener结束和返回的构造函数之间有一个小但有限的时间。 另一个线程可以使用,并尝试调用doSomething()。 如果运行时没有直接返回到您的代码,该对象可能处于无效状态。

我真的不确定Java,但我可以想到的一个例子是,在返回给您之前,运行时可能会重新定位实例。

我给你一个小小的机会。