1. 程式人生 > >建立執行緒安全的單例模式

建立執行緒安全的單例模式

單例模式的概念

單例模式就是確保只有一個例項,而且自行例項化並向整個系統傳遞這個例項,這個類就稱作單例類

單例模式最重要的一個特點就是構造方法私有化,單例模式分為懶漢式和餓漢式;

第一種:懶漢式(執行緒不安全)

傳統的懶漢式建立單例模式,是執行緒不安全的

public class Singleton {    
    private static Singleton instance;    
    private Singleton (){}    
    
    public static Singleton getInstance() {    
    if (instance == null) {    
        instance = new Singleton();    
    }    
    return instance;    
    }    
}    

懶漢式單例就是在呼叫的時候才去建立這個例項,這種寫法的懶載入很明顯,但缺點就是不能在多執行緒訪問下正常工作。

第二種:懶漢式(執行緒安全的)
public class Singleton {    
    private static Singleton instance;    
    private Singleton (){}    
    public static synchronized Singleton getInstance() {    
    if (instance == null) {    
        instance = new Singleton();    
    }    
    return instance;    
    }    
}

執行緒安全的方式建立單例就是在對外的建立例項方法上加上synchronized(同步),這種寫法能夠在多執行緒中很好的工作,且看起來也具備很好的lazy loading,但是,效率很低,99%情況下不需要同步。

第三種:懶漢式(執行緒安全的)
public class Singleton {    
    private static Singleton instance = new Singleton();    
    private Singleton (){}    
    public static Singleton getInstance() {    
    return instance;    
    }    
}    

這種方式基於classloader機制避免了多執行緒的同步問題,不過,instance在類裝載時就例項化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是呼叫getInstance方法,但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化instance下安然沒有達到lazy loading 的效果。

第四種:靜態內部類的方式建立單例模式
public class Singleton {  
    private Singleton() {  
    }  
    private static class SingletonHolder {// 靜態內部類  
        private static Singleton singleton = new Singleton();  
    }  
    public static Singleton getInstance() {  
        return SingletonHolder.singleton;  
    }  
}  

這中方式同樣利用;額classloader的機制來保證初始化instance時只有一個執行緒,它跟第三種方式不同的是(細微的差別):第三種方式是隻要singleton類被裝載了,那麼instance就會被初始化(沒有達到lazy loader效果),而這種方式是singleton類被裝載了,instance不一定被初始化,因為singleHolder類沒有被主動使用,只有顯示通過呼叫getInstance方法時,才會顯示裝載SingletonHolder類,從而例項化singleton。如果例項化singleton很消耗資源,想讓它延遲載入,另外一方面,不希望在singleton來載入時就例項化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被載入,那麼這個時候例項化instance顯然時不合適的,這個時候,這個時候,這種方式相比第三種方式就顯得很合理。

第五種:雙重校驗鎖
public class Singleton {  
  
    private static Singleton singleton;  
    private Singleton() {  
    }  
    public static Singleton getInstance(){  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    /** 
                     * 為什麼這裡會使用雙重判定呢? 
                     */  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}
這種是用雙重判斷來建立一個單例的方法,那麼為什麼要使用兩個if判斷當前是不是空的呢?因為當有多個執行緒同時要建立物件的時候,多個執行緒有可能都停止在第一個if判斷的地方,等待鎖的釋放,然後多個執行緒就都建立了物件,這樣就不是單例模式了,所以要用兩個if來進行這個物件是否存在的判斷。