java:为什么不允许这个转义构造函数?

时间:2012-11-21 23:11:49

标签: java concurrency

在JCIP的第3.2.1节“安全构造函数实践”中,警告不要将this从构造函数泄漏到另一个线程,“即使发布是构造函数中的最后一个语句。”最后一部分对我来说似乎太强大了,并没有任何理由。施工后发生了什么,我必须小心避免?有例外吗?我很感兴趣,因为我最近提交了一些代码,其中我做了这件事,我想决定是否有理由重新进行重构。

3 个答案:

答案 0 :(得分:4)

任何时候都不应该从构造函数中泄漏this,“甚至在最后一个语句中也是如此。”由于this没有完全构建,可能会发生一些非常奇怪的事情。有关非常类似的问题,请参阅this SO answer

答案 1 :(得分:4)

你永远不应该将this从构造函数中传出(称为“泄漏this”)

不应该这样做的一个原因,即使它是构造函数的最后一行,只要对当前线程的影响不受影响,就允许JVM重新排序语句。如果将this传递给在另一个线程中运行的进程,则重新排序会导致奇怪和微妙的错误。

另一个原因是子类可能提供自己的初始化,因此在类的构造函数的最后一行可能无法完成构造。

答案 2 :(得分:4)

就Java内存模型而言,构造函数出口在最终字段语义中起作用,因此语句是在构造函数退出之前还是之后存在差异。

This works                         This doesn't work
-------------------------------------------------------------

static Foo shared;                 static Foo shared;

class Foo                          class Foo
{                                  {   
    final int i;                       final int i; 
    Foo()                              Foo() 
    {                                  {
        i = 1;                             i = 1;
                                           shared = this;  
    }                                  }
}                                  }

shared = new Foo();                new Foo();

(注意:shared不易变;发布是通过数据竞争。)

两个示例之间的唯一区别是在构造函数退出之前或之后分配shared。在第二个示例中,允许在分配后重新排序i=1

但是,如果发布是同步动作,例如通过一个易变的变量,然后就可以了;其他线程将观察完全初始化的对象;这些字段甚至不必final

通过数据竞赛发布(或通过数据竞争做任何事情)是一项非常棘手的业务,需要非常谨慎的推理。如果你避免数据竞争,事情会简单得多。 如果您的代码不包含数据争用,则在构造函数退出之前立即泄漏this并在构造函数退出后立即发布它之间没有区别。