《設計模式》中對Singleton模式的定義為:保證一個類僅有一個實例,并提供一個可以訪問它的全局訪問點。
Singleton模式的工作原理如下:
- 用一個方法來實例化所需對象。為了保證該方法是唯一能實例化此類型對象的方法,我們將這個類的構造函數定義為保護的或者私有的。
- 調用這個方法時,先要檢查對象是否已經實例化了。如果已被實例化,那么該方法僅返回對該對象的引用;如果尚未實例化,該方法將實例化對象并返回對此新實例的一個引用。
根據上述原理,我們可以得到Singleton模式的本質在于所有使用所需對象的其他對象都使用同一個實例。Singleton模式的意圖使我們只對所需對象實例化一次,無需用戶再關心所需對象是否存在,即讓所需對象自己負責只實例化一次。
單例模式一般代碼如下:
1 public class Singleton {
2 static private Singleton instance = new Singleton();
3
4 private Singleton(){}
5
6 public static Singleton getInstance(){
7 return instance;
8 }
9 }
當ClassLoader加載類后Singleton的實例會在第一時間內創建,因此對于開銷比較大的單體更通常的做法是將實例化推遲到使用它的時候,即惰性加載,代碼如下:
1 public class Singleton {
2 static private Singleton instance = null;
3
4 private Singleton(){}
5
6 public static Singleton getInstance(){
7 if(instance == null) instance =new Singleton();
8 return instance;
9 }
10 }
這是在單線程中的情況,如果我們的代碼要在多線程中運行呢?
咋看起來,只需同步化對Singleton對象是否已創建進行檢查操作即可。但性能上來說不不劃算,因為所有線程都必須等待關于對象是否存在的檢查。因此可能想到的是如下解決方法:
1 if(instance == null) {
2 synchronized(Singleton.class){
3 instance = new Singleton();
4 }
5 }
但仍有可能兩個調用都滿足null的檢查條件,然后再嘗試同步化,最后還是可能會創建兩個Singleton對象。解決辦法是在同步檢查后,再次檢查實例是否已創建。
1 if(instance == null) {
2 synchronized(Singleton.class){
3 if(instance == null) instance = new Singleton();
4 }
5 }
這種方法被稱為DCL(Double-Checked Locking模式)。但是我們很快發現,這種方法在C++中適用,但對Java不適用,原因在于Java編譯器本身的優化工作會在構造方法實例化對象之前從構造方法返回指向該對象的引用。印在在Singleton對象真正完全構造之前,dosyn就可能完成了。那怎么辦呢?
我們可以利用類裝載程序來解決這個問題。
1 public class Singleton {
2 private static class Instance{
3 static final Singleton instance = new Singleton();
4 public static Singleton getInstance(){
5 return Instance.instance;
6 }
7 }
8 }
這個方案之所以奏效,是因為內部類(Instance)只被裝載一次,所以只會創建一個Singleton對象。
參考:
1.《設計模式解析》Singleton模式和DCL模式
2.探索設計模式之六——單例模式
posted on 2012-07-27 01:04
hqjma 閱讀(115)
評論(0) 編輯 收藏 所屬分類:
Design Pattern