運做方式是這樣的: 若是建立了一個對象,同時過一下子後決定再建立一個新對象,此時會得到以前已建立的對象, 而不是一個新對象。 java
注意, 普通構造函數沒法實現上述行爲,由於構造函數的設計決定了它必須老是返回一個新對象。git
和全局變量同樣, 單例模式也容許在程序的任何地方訪問特定對象。同時能夠保護該實例不被其餘代碼覆蓋。程序員
單例模式是一種建立型設計模式,可以保證一個類只有一個實例,並提供一個訪問該實例的全局節點。github
注意:能夠隨時調整限制並設定生成單例實例的數量,只需修改 獲取實例
方法,即 getInstance
中的代碼便可實現。算法
若是能將對象的全部共享狀態簡化爲一個享元對象,那麼享元模式就和單例模式相似。但這個兩個設計模式有兩個根本性的不一樣。數據庫
靜態類使用設計模式
/** * 靜態類單例實現 */ public class StaticSingleton { public static Map<String,String> hashMap = new ConcurrentHashMap<String, String>(); }
單例模式的實現方式比較多,主要是實現上是否支持懶漢模式、是否線程安全。安全
接下來經過不一樣的單例模式實現方式來解析單例模式。多線程
/** * 懶漢單例模式(線程不安全) */ public class SlobThreadUnsafeSingleton { private static SlobThreadUnsafeSingleton instance; /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private SlobThreadUnsafeSingleton() { } public static SlobThreadUnsafeSingleton getInstance() { if (ObjectUtils.isEmpty(instance)) { instance = new SlobThreadUnsafeSingleton(); } return instance; } }
這種懶漢單例模式知足了懶加載,但當多個訪問者同時獲取對象實例時(多進程),會致使多個實例並存,沒有達到單例模式的需求。架構
/** * 懶漢單例模式(線程安全) */ public class SlobThreadSafeSingleton { private static SlobThreadSafeSingleton instance; /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private SlobThreadSafeSingleton() { } public static synchronized SlobThreadSafeSingleton getInstance() { if (ObjectUtils.isEmpty(instance)) { instance = new SlobThreadSafeSingleton(); } return instance; } }
這種懶漢單例模式是線程安全的,但因爲鎖在方法上,全部的訪問都須要鎖佔用,會致使資源的浪費。非特殊狀況,不建議使用此種方式來實現單例模式。
/** * 餓漢單例模式(線程安全) */ public class EagerSingleton { private static EagerSingleton instance = new EagerSingleton(); /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private EagerSingleton() { } private static EagerSingleton getInstance() { return instance; } }
餓漢單例模式並非懶加載,簡單來講就是不管是否用到該類都會在程序啓動之初建立。這種方式會致使的問題相似一打開某個軟件,手機直接卡死(開啓了過多無用功能,致使內存不足)。
/** * 雙重校驗鎖單例模式(線程安全) */ public class DoubleCheckingLockingSingleton { private static volatile DoubleCheckingLockingSingleton instance; /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private DoubleCheckingLockingSingleton() { } public static DoubleCheckingLockingSingleton getInstance() { if (ObjectUtils.isNotEmpty(instance)) { return instance; } synchronized (DoubleCheckingLockingSingleton.class) { if (ObjectUtils.isEmpty(instance)) { instance = new DoubleCheckingLockingSingleton(); } } return instance; } }
雙重校驗鎖方式實現的單例模式是方法級鎖的優化,減小了部分獲取實例的耗時,同時也知足了懶加載。
/** * 靜態內部類單例模式(線程安全) */ public class StaticInnerSingleton { private static class SingletonHolder { private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton(); } /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private StaticInnerSingleton(){ } public static final StaticInnerSingleton getInstance() { return SingletonHolder.INSTANCE; } }
靜態內部類實現的單例模式,既保證了線程安全,也保證了懶加載,同時不會由於加鎖的方式致使性能開銷過大。
這主要是由於 JVM 虛擬機能夠保證多進程併發訪問的正確性,即一個類的構造方法在多線程環境下,能夠被正確加載。
所以,靜態類內部類的實現方式很是推薦使用。
/** * CAS「AtomicReference」單例模式(線程安全) */ public class CompareAndSwapSingleton { private static final AtomicReference<CompareAndSwapSingleton> INSTANCE = new AtomicReference<CompareAndSwapSingleton>(); private static CompareAndSwapSingleton instance; /** * 單例的構造函數必須永遠是私有類型,以防止使用`new`運算符直接調用構造方法 */ private CompareAndSwapSingleton() { } public static final CompareAndSwapSingleton getInstance() { for (; ; ) { CompareAndSwapSingleton instance = INSTANCE.get(); if (ObjectUtils.isNotEmpty(instance)) { return instance; } INSTANCE.compareAndSet(null, new CompareAndSwapSingleton()); return INSTANCE.get(); } } }
Java 併發庫提供了不少原子類來支持併發訪問的數據安全性:AtomicInteger
、 AtomicBoolean
、 AtomicLong
、AtomicReference
。
AtomicReference
能夠封裝引用一個實例,支持併發訪問,該單例方式就是使用該特色實現。
採用 CAS 方式的優勢就是不須要傳統的加鎖方式來保證線程安全,而是依賴 CAS 的忙等算法,依賴底層硬件的實現,來保證線程安全。相對於其餘鎖的實現沒有線程的切換和阻塞,也就沒有了額外開銷,所以能夠支持較大的併發。
固然,CAS 方式也存在缺點,忙等即若是一直沒有獲取到將會處於死循環中。
/** * 枚舉單例(線程安全) */ public enum EnumSingleton { INSTANCE; public void whateverMethod() { System.out.println("enum singleton method"); } }
約書亞·布洛克(英語:Joshua J. Bloch,1961年8⽉28⽇-),美國著名程序員,《Effective Java》做者。他爲Java平臺設計並實做了許多的功能,曾擔任Google的⾸席Java架構師(Chief Java Architect)。
《Effective Java》做者約書亞·布洛克推薦使用枚舉的方式解決單例模式,這種方式應該是平時最少用到的。
這種方式解決的單例模式的主要問題:線程安全、自由串行化、單一實例。
調用方式
@Test public void testEnumSingleton() { EnumSingleton.INSTANCE.whateverMethod(); }
這種寫法在功能上與共有域⽅法相近,可是它更簡潔,⽆償地提供了串⾏化機制,絕對防⽌對此實例化,即便是在⾯對複雜的串⾏化或者反射攻擊的時候。雖然這種方式尚未⼴泛採⽤,可是單元素的枚舉類型已經成爲實現單例 的最佳⽅法。
但這種⽅式在存在繼承場景下是不可⽤的。
getInstance
獲取實例的靜態方法來返回其所屬類的一個相同實例。獲取實例
方法必須是獲取單例對象的惟一方式。設計模式並不難學,其自己就是多年經驗提煉出的開發指導思想,關鍵在於多加練習,帶着使用設計模式的思想去優化代碼,就能構建出更合理的代碼。
源碼地址: https://github.com/yiyufxst/d...參考資料:
小博哥重學設計模式:https://github.com/fuzhengwei...
深刻設計模式:https://refactoringguru.cn/de...