线程安全的单例模式本质上其实也是单次初始化,所以可以用Balking模式:
class Singleton{
private static
Singleton singleton;
//构造方法私有化
private Singleton(){}
//获取实例(单例)
public synchronized static
Singleton getInstance(){
if(singleton == null){
singleton=new Singleton();
}
return singleton;
}
}
这个实现但是性能却很差,因为互斥锁synchronized将getInstance()方法串行化了,那有没有办法可以优化一下它的性能呢?
办法当然是有的,那就是DCL,一旦Singleton对象被成功创建之后,就不会执行synchronized(Singleton.class){},即此时getInstance()方法的执行路径是无锁的,从而解决了性能问题。
使用volatile禁止编译优化。获取锁后的二次检查,出于安全性。
class Singleton{
private static volatile
Singleton singleton;
//构造方法私有化
private Singleton() {}
//获取实例(单例)
public static Singleton
getInstance() {
//第一次检查
if(singleton==null){
synchronize{Singleton.class){
//获取锁后二次检查
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
也可以使用DCL优化性能,双重检查中的第一次检查,完全是出于对性能的考量:避免执行加锁操作,因为加锁操作很耗时。而加锁之后的二次检查,则是出于对安全性负责。双重检查方案在优化加锁性能方面经常用到,ReadWriteLock实现缓存按需加载功能时,也用DCL。
对于 T t = new T();
其实有如下字节码指令完成
_new 'org/openjdk/jol/T'
dup
INVOKESPECIAL org/openjdk/jol/T.<init> ()V
astore 1
return
线程一 new 到一半时,m=0,发生重排序
这时线程 2 来了!看到 t 已经指向了一个半初始化的实例了!
这个概率很小,但是并发如淘宝,都是可能发生的!所以必须要加!