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

时间:2009-10-19 12:30:40

标签: java constructor initialization thread-safety

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

6 个答案:

答案 0 :(得分:23)

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

     public class A {

      private boolean isIt;
      private String yesItIs;

      public A() {
        EventListener el = new EventListener() { ....};
        StaticListeners.register(el);
        isIt = true;
        yesItIs = "yesItIs";
      }
     }

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

  

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

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

答案 1 :(得分:16)

非常简单的例子:

public class Test
{
    private static Test lastCreatedInstance;

    public Test()
    {
        lastCreatedInstance = this;
    }
}

答案 2 :(得分:7)

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

if(obj == null)
{
  synchronized(something)
  {
     if (obj == null) obj = BuildObject(...);
  }
} 
// do something with obj

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

答案 3 :(得分: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; }
}

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

答案 4 :(得分:2)

Steve Gilham在评估为什么双重检查锁定被打破时是正确的。如果线程A进入该方法并且obj为null,则该线程将开始创建该对象的实例并将其指定为obj。线程B可能在线程A仍在实例化该对象(但未完成)时进入,然后将该对象视为非空但该对象的字段可能尚未初始化。部分构造的对象。

但是,如果允许关键字this转义构造函数,则可能出现相同类型的问题。假设您的构造函数创建一个对象的实例,该对象分叉一个线程,该对象接受您的对象类型。现在您的对象可能尚未完全初始化,即您的某些字段可能为null。您在构造函数中创建的对象对对象的引用现在可以将您引用为非null对象,但获取空字段值。

更多解释:

你的构造函数可以初始化你的类中的每个字段,但是如果在创建任何其他对象之前允许'this'转义,那么当其他线程查看时它们可以为null(或默认为primative),如果是1.它们是没有宣布最终或2.他们没有被宣布为易变的

答案 5 :(得分:0)

public class Test extends SomeUnknownClass{
    public Test(){
        this.addListner(new SomeEventListner(){
            @Override
            void act(){}
        });
    }
}

在此操作之后,Some Event Listener的实例将具有指向Test对象的链接,作为通常的内部类。

可在此处找到更多示例: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html