安全的对象构造过程
不要在构造过程使用this引用逸出。
可以使用工厂方法来防止this引用在构造过程中逸出。
public class SafeListener{
private final EventListener listener;
private SafeListener(){
listener = new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
};
}
public static SafeListener new Instance(EventSource source){
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}
这里没有看明白为什么使用了私有的构造方法就能避免this引用逸出了?个人觉得这样照样可以在里面使用this引用,一时还没有想明白。线程封闭
如果仅在单线程内访问数据,就不需要同步。这种技术被称为线程封闭,它是实现线程安全最简单的方式之一。
有三种线程封闭方式:Ad-线程封闭、栈封闭、ThreadLocal类。
Ad-线程封闭是指,维护线程封闭性的职责完全由程序实现了承担。
栈封闭式线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。如果在线程内部上下文中使用非线程安全的对象,那么该对象仍然是线程安全的。
ThreadLocal类是维持线程封闭性的一种更规范方法。ThreadLocal提供了get与set等访问接口或方法,这些方法都为每个使用该变量的线程都存有一份独立的副本,因此get总是返回当前执行线程在调用set时设置的最新值。ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>(){
public Connection initialValue(){
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection(){
return connectionHolder.get();
}
如果某个对象在被创建后其状态就不能被修改,那么这个对象就被称为不可变对象。不可变对象一定是线程安全的。一个对象所有的域都声明为final类型的,这个对象也仍然是可变的,因为final类型的域可以保存可变对象的引用。
当满足以下条件时,对象才是不可变的:
- 对象创建后其状态不能被修改。
- 对象所有域都是final类型的。
- 对象是正确创建的(在对象的创建期间,this引用没有逸出)。
如果类有任何可变对象引用,那么当它们在类和类的调用者间传递的时候必须保护性拷贝。
Final域
final类型的引用变量指向的引用对象,是不能重新指向其它的引用对象。final域能确保初始化过程的安全性,从而不受限制被访问,在共享对象时无须同步。
安全发布
对象安全地发布:
- 在静态初始化函数中初始化一个对象引用。
- 将对象的引用保存到volatile类型的域或者AtomicReferance对象中。
- 将对象的引用保存操某个正确构造对象的final类型域中。
- 将对象的引用保存到一个由锁保护的域中。
事实不可变对象
如果对象从技术上来看是可变的,但其状态在发布后不会再改变,那么把这种对象称为“事实不可变对象”。
在没有额外的同步的情况下,任何线程都可以使用被安全发布地事实不可变对象。
对象的发布需求取决于它的可变性:
- 不可变对象可以通过任意机制来发布。
- 事实不可变对象必须通过安全机制来发布。
- 可变对象必须通过安全机制来发布,并且是线程安全的或者由某个锁保护起来。
线程封闭。
只读共享。
线程安全共享。线程安全的对象内部实现同步。
保护对象。线程通过获得锁来访问。