1. 預熱
爲了小夥伴們能夠比較好的理解後面講解,先將一些前置的概念在這裏進行預熱,假如你已經知道這裏概念,你可以跳過這個小結。
- 指令序重排:Java內存模型中,允許編譯器和處理器對指令進行重排序,重排序過程不會影響到單線程程序的執行,會影響到多線程併發執行的正確性
- volatile 可見性生命:內存屏障和禁止重排序優化來實現
2. 實現方法分析
實現單例模式估計是全地球人都知道的設計模式了,它又有懶漢模式和餓漢模式之分,區別在於對單例對象初始化的時機不同,懶漢模式顧名思義,比較懶,所以對象的初始化會放到使用的那一個刻纔會進行!餓漢反之,具體孰好孰壞,這個得兩說,就餓漢來說,如果單例對象比較輕(廢話,要是很輕還用單例搞鬼麼)和初始化比較快且後續一定使用的話,這個就無所謂了!比較推薦,但是如果不用,這樣就有點浪費服務器的性能和資源了!接下來我會提供這兩種模式的幾種實現方式!
a) 懶漢模式
/** * 兩種經典的餓漢式實現 * * @author Lin.Jianfei * @version 1.0.0 * @since 2018-05-07 */ class Singleton4Eager1 { private Singleton4Eager1() {} private static Singleton4Eager1 INSTANCE = new Singleton4Eager1(); public static Singleton4Eager1 getInstance() { return INSTANCE; } } class Singleton4Eager2 { private Singleton4Eager2() {} // 需要注意靜態代碼的順序,靜態代碼塊是按照編寫的順序執行 private static Singleton4Eager2 INSTANCE = null; static { INSTANCE = new Singleton4Eager2(); } public static Singleton4Eager2 getInstance() { return INSTANCE; } }
b) 懶漢式
class Singleton4Lazy1 { private Singleton4Lazy1() {} private Singleton4Lazy1 instance = null; // 線程不安全 public Singleton4Lazy1 getInstance() { if (instance == null) { instance = new Singleton4Lazy1(); } return instance; } } class Singleton4Lazy2 { private Singleton4Lazy2() {} private Singleton4Lazy2 instance = null; // 線程安全,效率低下 public synchronized Singleton4Lazy2 getInstance() { if (instance == null) { instance = new Singleton4Lazy2(); } return instance; } } class Singleton4Lazy3 { private Singleton4Lazy3() {} // 這裏加上 volatile 修飾,禁止指令優化重排 private volatile Singleton4Lazy3 instance = null; // 線程安全,引入 volatile + 雙重校驗 改善併發性能, public synchronized Singleton4Lazy3 getInstance() { if (instance == null) { synchronized (Singleton4Lazy3.class) { if (instance == null) { instance = new Singleton4Lazy3(); } } } return instance; } } class Singleton4Lazy4 { private Singleton4Lazy4() {} private Singleton4Lazy4 instance = null; public Singleton4Lazy4 getInstance() { return InnerSingleton.INSTANCE.getInstance(); } private enum InnerSingleton { INSTANCE; private Singleton4Lazy4 instance; // 利用虛擬機特性,保證延遲且只執行一次創建,避免的使用鎖,性能最好 InnerSingleton() { this.instance = new Singleton4Lazy4(); } public Singleton4Lazy4 getInstance() { return this.instance; } } }
3. 總結
這裏,實現餓漢式有好多種方式可以實現,可以根據自己的業務場景需要進行選擇,如果是單線程的場景,無疑是第一種最好,類似StringBuilder 和 StringBuffer。假如涉及到併發的話,建議採用最後用枚舉方式的實現,即避免了鎖的損耗且延遲加載!