[java] 如何通過發佈內部類實例`this`引用外部類?



Answers

我會稍微修改一下這個例子,使其更加清晰。 考慮這個類:

public class ThisEscape {

    Object someThing;

    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e, someThing);
                }
            });
        someThing = initTheThing();
    }
}

在幕後,匿名內部類可以訪問外部實例。 你可以這樣說,因為你可以訪問實例變量someThing ,正如Shashank提到的,你可以通過ThisEscape.this訪問外部實例。

問題是,通過給外部匿名內部類實例(在這種情況下是EventSource對象),它也將攜帶ThisEscape實例。

會發生什麼事情呢? 考慮下面這個EventSource的實現:

public SomeEventSource implements EventSource {

    EventListener listener;

    public void registerListener(EventListener listener) {
        this.listener = listener;
    }

    public void processEvent(Event e) {
        listener.onEvent(e);
    }

}

ThisEscape的構造函數中,我們註冊了一個EventListener ,它將存儲在listener實例變量中。

現在考慮兩個線程。 一個是調用ThisEscape構造函數,另一個調用processEvent與一些事件。 另外,讓我們假設JVM決定從第一個線程切換到第二個線程,就在someThing = initTheThing()之後,在someThing = initTheThing() 。 第二個線程現在運行,它將調用onEvent方法,正如你所看到的,它用一些someThing做一些事情。 但是有什麼someThing ? 它是空的,因為另一個線程沒有完成初始化對象,所以這(可能)會導致NullPointerException,這實際上不是你想要的。

總結一下:小心你不要逃避尚未完全初始化的對象(換句話說,它們的構造函數還沒有完成)。 你可能無意中做到這一點的一個微妙的方式是從構造函數中匿名匿名內部類,這將隱式地轉義未完全初始化的外部實例。

Question

這個問題稍微有點不同, 但要求是/否的答案,但是我正在尋找這本書(Java Concurrency in Practice)所缺少的解釋,這個錯誤是如何被惡意或意外利用的。

可以發布對像或其內部狀態的最終機制是發布一個內部類實例,如清單3.7中的ThisEscape所示。 當ThisEscape發布EventListener時,它隱式地發布封閉的ThisEscape實例,因為內部類實例包含對封閉實例的隱藏引用

清單3.7。 隱式允許這個引用轉義。 不要這樣做。

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

3.2.1。 安全施工實踐

ThisEscape說明了一個重要的逃生特例 - 當這個參考文件在施工時逃脫。 當發佈內部EventListener實例時,封閉的ThisEscape實例也是如此。 但是一個對像只有在其構造函數返回後才處於可預測的一致狀態,所以從其構造函數中發布一個對象可以發布一個不完整構造的對象。 即使發布是構造函數中的最後一個語句,也是如此。 如果這個參考文件在施工過程中逃逸,則認為該物體構造不正確。

[8]更具體地說,直到構造函數返回之後,這個引用不應該從線程中轉義出來。 這個引用可以存儲在構造函數的某個地方,只要它在構造之後不被另一個線程使用。 程序清單3.8中的SafeListener使用了這種技術。

施工期間不要讓這個參考文件逃脫。

在完成構造之前,有人會如何編寫代碼來獲得OuterClass? 第一段斜體中提到的hidden inner class reference是什麼?




Links