双重检测锁实现的单例模式看起来非常完美,实则会出现同步问题。
public MyObject{
private static MyObect instance;
private Date d = new Data();
public Data getD(){return this.d;}
public static MyObect getInstance(){
if(instance == null){
synchronized(MyObect .class){ // 1
if(instance == null) // 2
instance = new MyObject(); // 3
}
}
return instance;
}
}
1处会将线程进行同步。
2处会再次检查是否已经被其他线程实例化。
3处会出现JVM内存模型允许所谓的无序写入
,如下
1: mem = allocate(); //Allocate memory for Singleton object.
2: instance = mem; //Note that instance is now non-null, but
//has not been initialized.
3: ctorSingleton(instance); //Invoke constructor for Singleton passing
在2之后 instance 给了一个空白空间 mem,已经不为空。
因为此时线程未离开 synchronized 块,所有 instance 所有的数据都未初始化且未同步。
当线程1离开 synchronized 块,如果由于线程间可见性问题可能导致其他线程 获取的 instance对象对应的 mem空间可能是 线程1未写回的空白空间,这时访问 getD()为空
这便是同步缺陷。
也可以使用贪婪方式,直接在类加载时初始化instance。
public MyObject{
private static MyObect instance = new MyObject();
private Date d = new Data();
public Data getD(){return this.d;}
public static getInstance(){
return instance;
}
}
Lazy-init的同步实现其实可以让jvm去实现。如下
public MyObject{
private static class InstanceHoder{ //内部私有的类。
static MyObject instance = new MyObject();
}
private Date d = new Data();
public Data getD(){return this.d;}
public static MyObect getInstance(){
return InstanceHoder.instance;
}
}
InstanceHoder内部类只有在使用到时候才会加载。