在日常工作中,有很多個物件,我們只需要一個。比如:線程池, 緩衝,註冊表等。如果製造出多個執行個體,就會導致許多問題,如程式行為異常,資源使用過量等。這就需要對對象的構建進行控制,使其只能產生一個對象。這就是本篇要講的設計模式——singleton(單例)。
單例模式的定義:確保只有一個類只有一個執行個體,並提供一個全域訪問點。
那麼,要如何?單例模式,使得一個類只能產生一個對象呢。請看下面的實現:
public class Singleton {private static Singleton s;private Singleton() {}public static Singleton getInstance() {if (s == null) {s = new Singleton();}return s;}}
你或許已經看懂了上面的代碼。沒錯。我們可以通過將類的建構函式設成private存取權限,這樣其他類就無法隨意建立該類的對象了。然後我們應用一個靜態類對象來作為唯一的一個對象,並作為全域訪問點。
clone()方法
還有一件事值得注意:類的clone()方法。例如, 如果基類實現了cloneable介面的話,子類就應該重寫該方法。防止類的對象被copy,破壞單例特性。當然,在應用中應該靈活運用各種方法來防止clone()的各種情況。
多線程訪問singleton方法
對於上面的實現方法,如果在多線程中被訪問,可能會產生問題。
public class Singleton {private static Singleton s;private Singleton() {}// 如果多個線程同時訪問, 有可能會出現多個執行個體。public static Singleton getInstance() {// 第一次初始化時,多個線程同時執行"if (s == null)",判斷結果都為真,所以都會執行下面的操作:"s = new Singleton()",由此引發多個執行個體的出現。if (s == null) {s = new Singleton();}return s;}}
那麼該如何解決單例模式在多線程程式中的問題呢。
方案1:
將getInstance()變成同步(synchronized)方法,多線程的災難可以輕易解決。
public class Singleton {private static Singleton s;private Singleton() {}public static synchronized Singleton getInstance() {if (s == null) {s = new Singleton();}return s;}}
不推薦該方案。 因為對單例初始化只需要一次,這樣做將會使得每次調用getInstance方法時都會進行同步。對運行效率是極大的累贅。
方案2:
在靜態初始化時建立單例。
public class Singleton {// Early initialization.定義的時候就初始化。private static Singleton s = new Singleton();private Singleton() {}public static Singleton getInstance() {return s;}}
該方法可以保證單例。但我們對程式設計時,通常保持直到使用時才建立對象的原則。
方案3:
用“雙重檢查加鎖”,在getInstance()中減少使用同步。
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;}} 這樣將只有第一次建立對象時同步,既保證了安全執行緒,同時又保證了執行效率。
有人問為什麼要在synchronized 內再一次判斷if(sinlenton == null),我個人理解是這個原因:
如果兩個線程都執行到了第一個if(singleton == null) 且都為true,這是總有一個線程先進入synchronized (Singleton.class)...部分代碼,並阻塞另一個進入。當第一個進入後並成功建了一個執行個體singleton後,不再阻塞。另一個線程將進入執行synchronized (Singleton.class)...代碼(因為它已經執行了第一個singleton==null 並為true)。如果裡面不再進行判斷,則會再一次建立一個singleton。
所以裡面再一次判斷了一次。
參考:
《head first 設計模式》
http://blog.csdn.net/natee/article/details/4408245#quote