雙重檢測鎖實(shí)現(xiàn)的單例模式看起來非常完美,實(shí)則會出現(xiàn)同步問題。
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處會將線程進(jìn)行同步。
2處會再次檢查是否已經(jīng)被其他線程實(shí)例化。
3處會出現(xiàn)JVM內(nèi)存模型允許所謂的無序?qū)懭?/code>,如下
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,已經(jīng)不為空。
因?yàn)榇藭r線程未離開 synchronized 塊,所有 instance 所有的數(shù)據(jù)都未初始化且未同步。
當(dāng)線程1離開 synchronized 塊,如果由于線程間可見性問題可能導(dǎo)致其他線程 獲取的 instance對象對應(yīng)的 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的同步實(shí)現(xiàn)其實(shí)可以讓jvm去實(shí)現(xiàn)。如下
public MyObject{
private static class InstanceHoder{ //內(nèi)部私有的類。
static MyObject instance = new MyObject();
}
private Date d = new Data();
public Data getD(){return this.d;}
public static MyObect getInstance(){
return InstanceHoder.instance;
}
}
InstanceHoder內(nèi)部類只有在使用到時候才會加載。