文章目錄
本文出自 http://blog.csdn.net/shuangde800
走進單件模式
有些對象我們只需要一個,比如:線程池,緩衝,對話方塊,處理喜好設定和註冊表的對象,日誌對象,充當印表機,顯卡等裝置的驅動程式對象等。
這些類的對象只能有一個執行個體,如果有多個,然而會導致許多問題產生。
有人可能覺得,這隻要用全域變數或者靜態變數就可以做到了
這樣做的缺點:
必須程式一開始就建立對象,但是有些對象可能用不到,耗費資源。而單件模式在需要時才建立
單間模式的實現
單件模式的經典但簡陋(有問題)的實現:
把建構函式設定為私人的,再加一個靜態方法用來建立對象並返回。
public Singleton{ private static Sigleton uniqueInstance; // 構造器是私人的,只有類內部方法才能建立類 private Singleton() { } public static MyClass getInstance(){ if(uniqueInstance == null){ // 只有是null的才建立對象 uniqueInstance = new Singleton(); } return uniqueInstance; }}
上面那個做法,會出現問題,原因是多線程產生的問題。
假如有兩個線程要幾乎同時要建立一個對象,那麼將會發生下面的情況:
看,發現Thread one和Thread two都各自建立了一個對象!
解決方案1:
把getInstance( )變成同步(synchronized)方法,多線程災難幾乎就可以輕易解決了。
public Singleton{ private static Sigleton uniqueInstance; // 構造器是私人的,只有類內部方法才能建立類 private Singleton() { } // 通過增加synchronized關鍵字,會迫使每個線程在進入這個 // 方法之前,要先等候別的線程離開該方法。 // 也就是說,不會有兩個線程可以同時進入這個方法 public static synchronized MyClass getInstance(){ if(uniqueInstance == null){ // 只有是null的才建立對象 uniqueInstance = new Singleton(); } return uniqueInstance; }}
缺點: 每次用該方法都要同步,會降低拖垮程式的效能。同步一個方法可能會造成程式執行效率下降100倍。
解決方案2:急切執行個體化
public Singleton{ // 在靜態初始化器(static initializer)中建立單件 // 保證了安全執行緒(thread safe) private static Sigleton uniqueInstance = new Singleton(); // 構造器是私人的,只有類內部方法才能建立類 private Singleton() { } public static synchronized MyClass getInstance(){ return uniqueInstance; }}解決方案3: 用“雙重檢查加鎖”,在getInstance()中減少使用同步
利用“雙重檢查加鎖”(double-checked locking),首先檢查執行個體是否已經建立了,如果尚未建立,“才”進行同步。
這樣,只有第一次會同步,之後再也不會了
public Singleton{ // 注意這裡加了volatile關鍵字 // volatile關鍵字確保:當uniqueInstance變數被初始化成Singleton執行個體時 // 多個線程正確地處理uniqueInstance變數 private static volatile Sigleton uniqueInstance; private Singleton() { } public static synchronized MyClass getInstance() { if(uniqueInstance == null) { synchronized (Singleton.class) { if(uniqueInstance == null){ uniqueInstance = new Singleton(); } } } return uniqueInstance; }}
注意! 雙重檢查加鎖不使用於java 1.4以及更早的版本。
定義單件模式
單件模式確保一個類只有一個執行個體,並提供一個全域訪問點
單件模式的類圖很簡單
全域變數 VS 單件模式
在Java中,全域變數基本上就是對對象的靜態引用。
在這樣的情況下使用全域變數會有一些缺點,我們已經提到了期中一個:急切執行個體化 VS.延遲執行個體化。
但是我們要記住這個模式的目的:確保類只有一個執行個體並提供全域訪問。
全域變數也會變相鼓勵開發人員,用許多全域變數指向許多小對象來造成命名空間(namespace)的汙染。
單件模式不鼓勵這樣的現象,但單件仍然可能被濫用。