this引用隐式逸出

1,314 阅读1分钟

内部类、匿名内部类可以访问外部类的对象的域,是因为内部类构造的时候,编译器会把外部类的对象this隐式地作为一个参数传递给内部类的构造方法。

例子:在构造函数中注册一个事件监听器(×)

ThisEscape发布匿名内部类EventListener时,隐含地发布了ThisEscape实例this,而且内部类EventListener实例中包含了对ThisEscape实例地隐含引用,即doSomething(e)方法可以修改 ThisEscape中的属性或者调用ThisEscape中地其他方法。

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

在多线程的环境下,如果A线程正在初始化ThisEscape的实例,在线程A还没完成初始化时,由于共享了构建内部类实例时逸出的外部类ThisEscape的实例this,线程B就可以通过this访问doSomething(e)方法,可能修改ThisEscape的属性,导致一些莫名其妙的现象。

解决问题的关键在于,要保证doSomething(e)方法一定要在外部类ThisEscape初始化完成后才能调用,即不要在构造过程中使this引用逸出。

改进:使用私有的构造函数和一个公共的工厂方法(√)

构造函数将this引用保存到某个地方,使其他线程不会在构造函数完成之前使用它。

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;
    }
}