【问题标题】:Reference of enclosing object escape through anonymous class- java通过匿名类-java引用封闭对象转义
【发布时间】:2011-02-26 20:38:23
【问题描述】:

我正在阅读实践中的 Java 并发,下面的示例来自于此。我的问题是 这种引用转义是什么意思?会有什么问题? . this 引用是如何从 doSomething(e) 中转义的。

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

这如何解决问题

public class SafeListener {
    private final EventListener listener;
    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }
    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

编辑:

我已经尝试了以下示例

public class Escape {
    public  Escape( Printer printer ){
        printer.print(new Escaper(){
            @Override
            public void parentData(){
            theCulprit1(Escape.this);
            }
            public String name = "shal";
            @Override
            public void theCulprit(){
            System.out.println( this.name );
            System.out.println( Escape.this.age );
            }
        });
        canAccess();
    }
    public void  canAccess(){
    this.age = "25";
    }
    public String age = "62";
    @SuppressWarnings("unused")
    public static void main(String args[]){
    Escape escape = new Escape(new Printer());
    }
}

class Printer{
    public void print(Escaper escaper){
    escaper.theCulprit();
    escaper.parentData();
    }
}

class Escaper{
    public void parentData(){
    }
    public void theCulprit(){
    }
    public void theCulprit1(Escape escape){
    System.out.println(escape.age);
    }
}

由于逃生对象构造不完整 这输出 shal 62 62

当我像这样更改我的代码时

public class Escape {
    private final Escaper escaper;
    private Escape( ){
        escaper = new Escaper(){
            @Override
            public void parentData(){
            theCulprit1(Escape.this);
            }
            public String name = "shal";
            public void theCulprit(){
            System.out.println( name );
            System.out.println( age );
            }
        };
        canAccess();
    }
    public void  canAccess(){
    age = "25";
    }
    public String age = "62";
    public static Escape newInstance( Printer printer){
    Escape escape = new Escape();
    printer.print(escape.escaper);
    return escape;
    }
    @SuppressWarnings("unused")
    public static void main(String args[]){
    Escape.newInstance(new Printer());
    }
}

在这里。它输出 shal 25 25

我说的对吗? 还有任何操作的重新排序,因为在第一个示例中,年龄被初始化为 62。 即使在我的第二个示例中没有使逃逸者字段成为最终状态,它也可以工作!

【问题讨论】:

  • 语言?平台?你到底在这里问什么?

标签: java concurrency reference this anonymous-class


【解决方案1】:

在第一种形式中,事件侦听器对象在在构造函数中注册到事件源,因此它使自己(以及通过关联“this”对象)可用于事件源之前构造函数完成。如果内部类对象逃逸,外部对象也会逃逸。

为什么会出现这个问题?一旦注册了事件监听器,事件源就可以随时调用它的方法。想象一下,事件源正在使用的线程开始调用事件侦听器方法。这现在甚至可以在构造函数完成之前发生。

然而,由于可见性问题,这个问题比看起来更糟糕。 即使您将注册作为构造函数执行的“最后一个操作”,仍然有可能看到部分构造对象或处于无效状态的对象。如果没有适当的先发生顺序,则根本无法保证可见性。

将它声明为 final 提供了在排序之前发生的情况(因此是第二种形式)。

【讨论】:

  • 感谢您的解释。理论上我可以理解您所说的。我尝试了一些程序来证明你在说什么。但我做不到。你能给我一个工作的例子吗?转义这个引用会如何产生这样的影响
  • 在单线程情况下不存在可见性问题。当多个线程交错时,它们就会显现出来。即便如此,要可靠地重现这些问题也不是那么容易。失败将是罕见且随机的。
【解决方案2】:

当你有一个非静态的内部类时,比如

class Outer {
    class Inner {
    }
}

内部类有一个引用外部类的隐藏字段,所以你可以想象它是这样的

class Outer {
    class Inner {
        Outer hiddenOuter;
    }
}

所以无论在哪里使用内部对象,外部对象都是可引用的,因此它的生命周期至少与内部对象一样长。

【讨论】:

  • +1:Java concurrency in practice这本书的书名似乎是sjlee提到的原因,但这里还有一个重点:内部对象用作EventListener确保在从源中移除内部对象(或收集源本身)之前,不会对外部对象进行垃圾回收。
  • 谢谢。以前没想过,现在明白了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-10
  • 2019-03-16
  • 1970-01-01
  • 2012-03-15
  • 1970-01-01
相关资源
最近更新 更多