"this"如何在Java中逃避构造函数?

non*_*tor 20 java constructor initialization thread-safety

我听说过由于构造不正确的对象而在非线程安全的代码中发生这种情况,但即使在Goetz的书中阅读之后我也没有理解这个概念.我想巩固我对这种代码气味的理解,因为我可能会这样做,甚至没有意识到.请在解释中提供代码以使其粘贴,谢谢.

KLE*_*KLE 24

示例:在构造函数中,您创建一个事件侦听器内部类(它具有对当前对象的隐式引用),并将其注册到侦听器列表.
=>所以你的对象可以被另一个线程使用,即使它没有完成执行它的构造函数.

     public class A {

      private boolean isIt;
      private String yesItIs;

      public A() {
        EventListener el = new EventListener() { ....};
        StaticListeners.register(el);
        isIt = true;
        yesItIs = "yesItIs";
      }
     }
Run Code Online (Sandbox Code Playgroud)

稍后可能发生的另一个问题:对象A可以完全创建,可供所有线程使用,由另一个线程使用...除了该线程可以看到A实例为创建,yesItIs带有"yesItIs"值,但是不isIt!信不信由你,这可能发生!会发生什么:

=> 同步只是阻塞线程的一半,另一半是关于线程间可见性.

Java选择的原因是性能:如果所有数据都与所有线程共享,则线程间可见性会破坏性能,因此只保证同步数据被共享...


Jon*_*eet 17

非常简单的例子:

public class Test
{
    private static Test lastCreatedInstance;

    public Test()
    {
        lastCreatedInstance = this;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @non sequitor:想象一下,如果构造函数中有很多其他初始化......但另一个线程在构造函数完成之前使用“lastCreatedInstance”。它可能使用半初始化的对象。 (2认同)

Ste*_*ham 7

这就是为什么双重检查锁定不起作用的原因.天真的代码

if(obj == null)
{
  synchronized(something)
  {
     if (obj == null) obj = BuildObject(...);
  }
} 
// do something with obj
Run Code Online (Sandbox Code Playgroud)

不安全,因为对局部变量的赋值可以在构造的其余部分(构造函数或工厂方法)之前发生.因此,线程1可以在BuildObject步骤中,当线程2进入同一块时,检测到非空obj,然后继续操作不完整的对象(线程1已经在呼叫中调度出来).

  • 您的链接包括 - "因此,调用getHelper()的线程可以看到对辅助对象的非空引用,但是请参阅辅助对象的字段的默认值,而不是构造函数中设置的值." 这是一个部分构建的对象. (5认同)
  • 这是你错的地方 - 分配可以在施工完成之前进行.有关完整讨论,请访问http://www.ibm.com/developerworks/java/library/j-dcl.html. (2认同)

Mic*_*rdt 5

public class MyClass{
    String name;    

    public MyClass(String s)
    {
        if(s==null)
        {
            throw new IllegalArgumentException();
        }
        OtherClass.method(this);
        name= s;
    }

    public getName(){ return name; }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,OtherClass.method()传递的实例MyClass是在那个点上未完全构造的,即尚未履行name属性为非null 的契约.