單件模式 Singleton
什麼時候使用?當需要獨一無二的對象時,請想起他。
舉例:線程池(threadpool),緩存(cache),對話框,處理偏好設置和注冊表(registry)的對象,驅動程序對象。
無需具體例子,先看類圖:包含一個private的自己的實例。private的構造函數,確保無法在類以外創建。在getInstance()中檢測私有實例是否創建,未則創建,若已存在則直接返回。
看代碼更好理解記憶。
經典實現方式 Typical Singleton
public class Singleton { private static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
經典實現看似完美,但存在問題。它不是線程安全的。當兩個線程同時調用的時候,有可能同時進入 uniqueInstance == null 的if塊中。這樣對象就不是唯一了。後進入的線程會獲得一個新的instance。解決方法,馬上直觀想到,使用synchronized確保線程安全。
Singleton Lazy Initialized (延遲加載)
public class Singleton { private static Singleton uniqueInstance; private Singleton() {} public static synchronized Singleton getInstance { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
如果對性能要求非常高,以上synchronized確實每次都要判斷有性能擔憂。可以改進如下。
Singleton Eagerly Initialized (急切創建)
public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() {} public static synchronized Singleton getInstance { return uniqueInstance; } }
很好的解決問題了。以上利用了JVM加載機制。在加載類的時候就吧實例創建好。除此,還有一種方法。
Singleton Double-checked Locking (雙重檢查鎖)
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
以上使用了volatile關鍵字,使uniqueInstance是線程同步讀的。即線程總能讀到最新的值。uniqueInstance為null時才進行創建。使用synchrinized確保線程安全。同時在塊內再次檢測是否需要創建。