1、不要在构造过程中使this引用逸出。当且仅当对象的构造函数返回时,对象才处于可预测和一致的状态,当从对象的构造函数中发布对象时,只是发布了一个尚未构造完成的对象。只有当构造函数返回时,this引用才应该从线程中逸出,构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数之前使用它。如果想在构造函数中注册事件监听器或者启动线程,可以使用一个私有的构造函数和公共的工厂方法。
public class SafeListener { private final EventListener listener; private SafeListener(){ listener = new EventListener(){ public void onEvent(Event e){ //TODO } }; } public static SafeListener newInstance(EventSource source){ SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe; }}
2、线程封闭(Thread Confinement),JDBC中的Connection对象就使用了线程封闭技术。JDBC规范并不要求Connection是线程安全的,但是连接池将其分配给一个线程之后,到Connection返回给连接池之前,连接池不会再把该Connection分配给其他线程使用,因此相当于线程封闭了;ThreadLocal也是线程封闭的。 AD HOC线程封闭,很少用(未懂是什么)。 栈封闭:只能通过局部变量才能访问对象(局部变量被封闭在执行线程中,因此自然的封闭了)。书中的例子:
public int loadTheArk(Collectioncandidates){ SortedSet animals; int numPairs = 0; Animal candidate = 0; //animals封闭在方法中,不要使其逸出 animals = new TreeSet (new SpecialGenderComparator()); 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; }
线程封闭性更规范的方法:ThreadLocal,防止可变的单实例变量(Singleton)或全局变量进行共享。
private static ThreadLocalconnectionHolder = new ThreadLocal (){ public Connection initialValue(){ Connection conn = null; try { conn = DriverManager.getConnection("URL"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } }; public static Connection getConnection(){ return connectionHolder.get(); }
可以用来保存事务上下文,避免调用每个方法时都要传递执行上下文信息。这样也会使运用了该机制的框架代码与一般代码耦合在一起。滥用ThreadLocal类似于全局变量,降低代码可重用性,并在类之间引入隐含的耦合性。
3、不变性:不可变对象一定是线程安全的,所有的域都是final类型的,对象也未必是不可变的,因为final类型的域可以保存可变对象的引用。符合以下条件则不可变: .)对象创建以后其状态就不能修改; .)对象的所有域都是final类型; .)对象都是正确创建的(在对象创建期间,this引用没有逸出)。 “不可变的对象”与“不可变的对象引用”之间存在差异,保存在不可变对象中的程序状态依然可以更新,可通过一个保存新状态的实例来替换原有的不可变对象。 大神们说的良好变成习惯:除非需要更高的可见性,否则将所有的域都声明为私有域;除非需要某个域是可变的,否则应将其声明为final域。 可以使用指向不可变容器对象的volatile类型引用以缓存最新的结果。
4安全发布:安全发布一个对象,对象引用和对象状态必须同时对其他线程可见,一个正确构造的对象可以通过以下方式安全发布: .)在静态初始化函数中初始化一个对象引用; .)将对象的引用保存到volatile类型的域或者AtomicReferance对象中; .)将对象的引用保存到某个正确构造对象的final类型域中。 .)将对象的引用保存到一个由锁保护的域中。