2

this reference escape during construction problem(由 Brian Goetz 和其他人在 Java Concurrency in Practice 中调用)是否影响单线程程序或仅影响多线程程序?我的意思是,如果我的类不应该是线程安全的,那么在构造过程中让 this 引用转义是否可以?

编辑:例如,在这里:

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

EDIT2:我的问题的动机是Eclipse的插件WindowBuilder在JFrame的构造函数中创建(或似乎创建......)动作侦听器,并默认将匿名类传递给那些,从而允许this引用转义......

4

3 回答 3

4

一般来说,在完全构建之前,您不应该尝试使用您的对象。如果您在对象完全初始化之前将其传递给其他代码,这可能会导致令人困惑的结果,除非您非常小心。即使在单线程程序中。

也就是说,有时这不是问题,尤其是如果您知道该对象直到以后才会使用。

于 2013-06-01T22:00:01.867 回答
3

允许“this”引用转义不一定会导致问题。它可以正常工作,但它完全取决于其他代码对它的作用。

那么为什么最好不要在构造函数中泄漏 this 呢?如果 EventSource 立即从 register 函数中发送“now connected”事件会怎样。怎么了?doSomething() 被调用(请注意,您仍处于构造函数的中间,可能尚未完成对象的初始化)。这完全取决于该方法的作用,但以下情况之一是正确的:

  1. 你在 doSomething 中需要的一切都在监听器注册之前被初始化,然后事情就解决了。
  2. 有些东西是空的,你忽略它,导致意想不到的结果
  3. 由于某些不应该为 null 的东西(NPE 或类似)而导致某些东西爆炸。

但是,你说,它不会发送那个事件。所以你没事吧?好吧,直到其他人更改它,并且它开始发送类似的内容,然后您在没有直接更改代码的情况下自发中断。

简短形式:如果可以,请避免这样做;它会引起头痛。如果出于某种原因绝对必须这样做,请注意在泄漏之前尽可能初始化所有内容,并非常小心地了解将要发生的事情,并意识到未来可能会出现麻烦。

于 2013-06-01T22:14:42.350 回答
1

不要这样做。在构造之前让引用转义可能会导致未指定的行为。使用静态工厂模式,让您的后顾之忧,多线程与否

在您的示例中,它看起来像这样:

public class ThisEscape{
  private final EventListener listener;

  private ThisEscape(){
    listener = new EventListener();
  }

  public static public ThisEscape makeEscape(EventSource source){
    ThisEscape e = new ThisEscape();
    source.registerListener(e.listener);
    return e;
  }

  private class EventListener{
    public void onEvent(Event e){
      doSomething(e);
    }
  }
}

显然静态工厂不允许无缝的类扩展,但根据我的经验,这通常不是问题,即使需要制作一些工厂方法。

该代码在构造方面是线程安全的;请务必记住,您的课程的其余部分是线程安全的。

于 2013-06-04T02:22:31.947 回答