发布一个对象的意思是使它能够被当前范围之外的代码所使用。比如将一个引用存储到其他代码可以访问的地方,在一个非私有的方法中返回这个引用。在很多情况下,我们需要确保对象及它们的内部状态不被暴露。一个对象在尚未准备好时就将它发布,称作逸出。
最常见的发布对象的方式就是将对象的引用存储到公共静态域中,任何类和线程都能看到这个域。
public static Set<Secret> knownSecrets;
public void initialize(){
knownSecrets=new HashSet<Secret>();
}
发布一个对象还会间接地发布其他对象。比如上面例子中加入 set 的 Secret 对象。
类似的,从非私有方法中返回引用,也能发布返回的对象。
class UnsafeStates{
private String[] states=new String[]{
“AK”,”AL”...
};
public String[] getStates(){
return states;
}
}
上面的例子中,
states
已经变成公有的了。
public class ThisEscape{
public ThisEscape(EventSource source){
source.registerListener(
new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
}
);
}
}
上面的例子演示了 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;
}
}
一个可以避免同步的方式就是不共享数据。如果数据仅在单线程中被访问,就不需要任何同步。线程封闭技术是实现线程安全的最简单的方式之一。当对象封闭在一个线程中时,这种做法是线程安全的,即使被封闭的对象本身不是线程安全的。
Swing的可视化组件和数据模型对象并不是线程安全的。通过将他们限制到Swing的事件分发线程中,实现线程安全的。
另一种常见的使用线程限制的应用程序是应用池化的JDBC,这种连接管理模式隐式地将Connection对象限制在处于请求处理期间的线程中。
pulbic int loadTheArk(Collection<Animal> candidates){
SortedSet<Animal> animals;
int numPairs=0;
Animal candidate=null;
//animals被限制在方法中,不要让他们逸出
animals=new ThreeSet<Animal>(new SpeciesGenderComparator());
animals.addAll(candidates);
for(Animal a:animals){
if(candidate==null||!candidate.isPotentialMate(a))
candidate=a;
else{
ark.load(new AnimalPair(candidate,a));
++numPairs;
candidate=null;
}
}
return numPairs;
}