最近在看《Java 並發編程實踐》看到3.2章里面的關於發布和逸出的部分,寫一下心得,算是mark一下,主要是構造過程中this引用的逸出。
書上面給出了一個構造過程中this逸出的例子:
public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener(new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } void doSomething(Event e) { } interface EventSource { void registerListener(EventListener e); } interface EventListener { void onEvent(Event e); } interface Event { } }
這將導致this逸出,所謂逸出,就是在不該發布的時候發布了一個引用。在這個例子里面,當我們實例化ThisEscape對象時,會調用source的registerListener方法,這時便啟動了一個線程,而且這個線程持有了ThisEscape對象(調用了對象的doSomething方法),但此時ThisEscape對象卻沒有實例化完成(還沒有返回一個引用),所以我們說,此時造成了一個this引用逸出,即還沒有完成的實例化ThisEscape對象的動作,卻已經暴露了對象的引用。其他線程訪問還沒有構造好的對象,可能會造成意料不到的問題。
最后,書里面給出了正確構造過程:
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; } void doSomething(Event e) { } interface EventSource { void registerListener(EventListener e); } interface EventListener { void onEvent(Event e); } interface Event { } }
在這個構造中,我們看到的最大的一個區別就是:當構造好了SafeListener對象(通過構造器構造)之后,我們才啟動了監聽線程,也就確保了SafeListener對象是構造完成之后再使用的SafeListener對象。
對於這樣的技術,書里面也有這樣的注釋:
具體來說,只有當構造函數返回時,this引用才應該從線程中逸出。構造函數可以將this引用保存到某個地方,只要其他線程不會在構造函數完成之前使用它。