关于发布和逸出
并发编程实践中,this引用逃逸(”this”escape)是指对象还没有构造完成,它的this引用就被发布出去了。这是危及到线程安全的,因为其他线程有可能通过这个逸出的引用访问到“初始化了一半”的对象(partially-constructed object)。这样就会出现某些线程中看到该对象的状态是没初始化完的状态,而在另外一些线程看到的却是已经初始化完的状态,这种不一致性是不确定的,程序也会因此而产生一些无法预知的并发错误。在说明并发编程中如何避免this引用逸出之前,我们先看看一个对象是如何产生this引用逸出的。
一、this引用逸出是如何产生的 正如代码清单1所示,ThisEscape在构造函数中引入了一个内部类EventListener,而内部类会自动的持有其外部类(这里是ThisEscape)的this引用。source.registerListener会将内部类发布出去,从而ThisEscape.this引用也随着内部类被发布了出去。但此时ThisEscape对象还没有构造完成 —— id已被赋值为1,但name还没被赋值,仍然为null。 代码清单1 this引用逸出示例
public class ThisEscape { public final int id; public final String name; public ThisEscape(EventSource<EventListener> source) { id = ; source.registerListener(new EventListener() { public void onEvent(Object obj) { System.out.println("id: "+ThisEscape.this.id); System.out.println("name: "+ThisEscape.this.name); } }); name = "xingpc"; } }
二、如何避免this引用逸出 上文演示了由内部类导致的this引用逸出是怎样产生的。它需要满足两个条件:一个是在构造函数中创建内部类(EventListener),另一个是在构造函数中就把这个内部类给发布了出去(source.registerListener)。因此,我们要防止这一类this引用逸出的方法就是避免让这两个条件同时出现。也就是说,如果要在构造函数中创建内部类,那么就不能在构造函数中把他发布了,应该在构造函数外发布,即等构造函数执行完毕,初始化工作已全部完成,再发布内部类。
public class ThisSafe { public final int id; public final String name; private final EventListener listener; private ThisSafe() { id = ; listener = new EventListener(){ public void onEvent(Object obj) { System.out.println("id: "+ThisSafe.this.id); System.out.println("name: "+ThisSafe.this.name); } }; name = "flysqrlboy"; } public static ThisSafe getInstance(EventSource<EventListener> source) { ThisSafe safe = new ThisSafe(); source.registerListener(safe.listener); return safe; }}