发布:
使对象能够在当前作用域之外的代码中使用。例如,讲一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用
逸出:
发布内部状态可能会破坏封装性,并使得程序难以维持不变性条件;当某个不应该发布的对象被发布时,这种情况就称为逸出
class UnsafeRelease{
private String[] states = new String[]{"hehe","pangda"};
public String[] getStates(){
return states;
}
}**
按照这种方式发布,任何调用者都能修改这个数组的内容,因为数组states已经逸出,当发布一个对象时,在该对象的非私有域中引用的对象同样会被发布。
不要在构造过程中使this引用逸出:
public class ThisEscape{
public ThisEscape(EventSource source){
source.registerListener(new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
})
}
}
这是一个特例,但是我们应该知道,在内部类中隐含有一个指向外部类的引用,所以当内部类被发布时外部类也隐含着被发布了,但是在构造函数中发布对象时,只是发布了一个尚未构造完成的对象,这将破坏线程安全性。
封装:
所以这也是为什么要使用封装的原因,当某个对象逸出后,假设某个类或线程可能会误用该对象;而封装能使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得更难。
安全的对象构造:
当对象在其构造函数中创建一个线程时,无论是显示创建还是隐式创建,this引用都会被新创建的线程所共享;而在对象未构造完成之前,新的对象也能看见他,如果对象未构造完成就被其他线程调用,线程安全性就被破坏了。
特别是隐式的引用的时候, 内部类含有对外部类的隐式引用
线程封闭:
当访问共享的可变数据时,通常需要使用同步 而线程封闭就是一种避免使用同步的方式;如果仅在单线程内访问数据,就不需要同步;当某个对象被封闭在一个线程中时,即除了事件线程之外的线程不能访问这些对象,这将自动获得线程安全性,即使封闭的对象本身是不安全的
通常使用线程封闭是为了将某个特定的子系统实现为一个单线程子系统;Java语音及核心内库提供帮助维持线程封闭性的机制,例如局部变量和Thre-adLocal类
三种常见的封闭方式:
当决定用线程封闭技术时,通常是因为要将某个特定的子系统实现为一个单线程子系统;
Ad-hoc线程封闭
Ad-hoc线程封闭是指,维护线程封闭性的职责完全由程序来完成; 这是非常脆弱的一种封闭,因为没有任何一种语言机制(局部变量和可见性修饰符)能将对象封闭到目标线程上
栈封闭
栈封闭是线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。因为局部变量的固有属性之一就是封闭在执行线程中;它们位于执行线程的栈中,其他线程无法访问这个栈(Java为每个线程单独分配一个栈)。 栈封闭比Ad-hoc更易维护,也更具有健壮性
public int loadTheArk(Collection<Animal> candidates) {
SortedSet<Animal> animals;
int numPairs = 0;
Animal candidate = null;
//把animals封闭在方法中
animals = new TreeSet<Animal>(new SpeciesGenderComarator());
animals.addAll(candidates);
for(Animal a : animals) {
if(candidate == nulll || !candidate.isPotentialMate(a))
candidate = a;
else {
ark.load(new AnimalPair(candidate,a));
++numPairs;
candidate = null;
}
}
return numPairs;
}
上述代码中的numPairs无论如何也不会破坏栈封闭性;由于任务方法都无法获得对基本类型的引用,因此Java语言的这种语义就确保了基本类型的局部变量始终封闭在线程内。
ThreadLocal类
ThreadLocal类能将线程的某个值和使用这个值的对象关联起来。ThreadLocal提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get方法总能取到最新的值 通常用于防止对可变的单实例变量或全局变量进行共享
在多线程应用程序中在没有协同的情况下使用全局变量时,就不是线程安全的,通过将JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有自己的连接. 如下代码:
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
在volatile变量上存在一种特殊的线程封闭,但前提是能确保只有单个线程对共享的volatile变量执行写入操作,那么就可以安全的在这些共享的volatile变量上执行“读取-修改-写入”操作。