ass EventListener2 {
public EventListener2(EventSource eventSource) {
eventSource.registerListener(
new EventListener() {
public onEvent(Event e) {
eventReceived(e);
}
});
}
public onEvent(Event e) {
}
}
EventListener2 类和其类似代码 清单 2 中的 EventListener 有同样的弊端:公布了对正在构造的对象的引用 ― 在这种情况下,是间接的 ― 另一个线程可以看见这个引用。如果打算创建 EventListener2 的子类,将会碰到同样的问题,即在子类构造函数完成之前会调用子类方法。
不要从构造函数内启动线程
清单 4 问题的一个特例是,从构造函数内启动了一个线程,由于当一个对象拥有一个线程时,通常这个线程要么是一个内部类,要么我们将 this 引用传递给其构造函数(或者该类本身继承了 Thread 类)。如果一个对象将拥有一个线程,那么,如果对象如 Thread 那样提供一个 start() 方法,并从 start() 方法而不是从构造函数启动线程,那是最好的。虽然通过接口,这会暴露类的一些实现细节(譬如,可能存在拥有一个线程),这往往是我们不期望的,但在这种情况下,从构造函数启动线程的害处要比隐藏实现所带来的好处多。
“公布”是指什么?
在构造期间,不是所有对 this 引用的引用都是有害的,只有那些公布引用,使其它线程看到该引用的引用才是有害的。确定与其它对象共享 this 引用是否安全,需要详细了解对象的可见性以及对象将如何利用引用。关于让 this 引用在构造期间逃脱,清单 5 列出了一些安全和不安全做法的示例:
清单 5. 在构造期间,安全和不安全地使用“this”引用
public class Safe {
private Object me;
private Set set = new HashSet();
private Thread thread;
public void run() {
// Safe because "me" is not visible from any other thread
me = this;
// Safe because "set" is not visible from any other thread
set.add(this);
// Safe because MyThread won''t start until construction is complete
// and the constructor doesn''t publish the reference
thread = new MyThread(this);
}
public void run() {
thread.start();
}
private class MyThread(Object o) {
private Object theObject;
public MyThread(Object o) {
this.theObject = o;
}
...
}
}
public class Unsafe {
public static Unsafe anInstance;
public static Set set = new HashSet();
private Set mySet = new HashSet();
public void run() {
// Unsafe because anInstance is globally visible
anInstance = this;
// Unsafe because SomeOtherClass.anInstance is globally visible
SomeOtherClass.anInstance = this;
// Unsafe because SomeOtherClass might save the "this" reference
// where another thread could see it
SomeOtherClass.registerObject(this);
// Unsafe because set is globally visible
set.add(this);
// Unsafe because we are publishing a reference to mySet
mySet.add(this);
SomeOtherClass.someMethod(mySet);
// Unsafe because the "this" object will be visible from the new
// thread before the constructor completes
thread = new MyThread(this);
thread.start();
}
public void run() {
// Unsafe because &quo
|