1. 程式人生 > >設計模式:單例模式(Singleton)

設計模式:單例模式(Singleton)

  單例模式在23個設計模式中算得上是最簡單的一個了,也許你會有異議,那就換成“最簡單之一”,這樣就嚴謹了很多。
  單例模式:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
  適用性:當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時。當這個唯一例項應該是通過子類化可擴充套件的,並且客戶應該無需更改程式碼就能使用一個擴充套件的例項時。
這裡寫圖片描述

單例模式有5中寫法(執行緒安全):
1. 餓漢式
2. 懶漢式
3. 雙檢索(DCL)
4. 佔位符式
5. 列舉式
下面分別展示這五種寫法(詳細內容可以參考博主的《singleton模式四種執行緒安全的實現》和《如何防止單例模式被JAVA反射攻擊

》)

餓漢式

public class EagerSingleton {  
        // jvm保證在任何執行緒訪問uniqueInstance靜態變數之前一定先建立了此例項  
        private static EagerSingleton uniqueInstance = new EagerSingleton();  

        // 私有的預設構造子,保證外界無法直接例項化  
        private EagerSingleton() {  
        }  

        // 提供全域性訪問點獲取唯一的例項  
        public
static EagerSingleton getInstance() { return uniqueInstance; } }

懶漢式

public class LazySingleton {  
        private static LazySingleton uniqueInstance;  

        private LazySingleton() {  
        }  

        public static synchronized LazySingleton getInstance
() { if (uniqueInstance == null) uniqueInstance = new LazySingleton(); return uniqueInstance; } }

雙檢鎖

public class DoubleCheckedLockingSingleton {  
        // java中使用雙重檢查鎖定機制,由於Java編譯器和JIT的優化的原因系統無法保證我們期望的執行次序。  
        // 在java5.0修改了記憶體模型,使用volatile宣告的變數可以強制遮蔽編譯器和JIT的優化工作  
        private volatile static DoubleCheckedLockingSingleton uniqueInstance;  

        private DoubleCheckedLockingSingleton() {  
        }  

        public static DoubleCheckedLockingSingleton getInstance() {  
                DoubleCheckedLockingSingleton localRef = uniqueInstance;
                if (uniqueInstance == null) {  
                        synchronized (DoubleCheckedLockingSingleton.class) {  
                                localRef = uniqueInstance;
                                if (localRef == null) {  
                                        uniqueInstance = localRef = new DoubleCheckedLockingSingleton();  
                                }  
                        }  
                }  
                return localRef ;  
        }  
}

注意上面的變數localRef,“似乎”看上去顯得有點多餘。但是實際上絕大多數時候uniqueInstance已經被初始化,引入ocalRef可以使得volatile的只被訪問一次(利用return localRef代替return helper),這樣可以使得這個單例的整體效能提升25%。更多可以參考wiki百科

佔位符式

public class LazyInitHolderSingleton {  
        private LazyInitHolderSingleton() {  
        }  

        private static class SingletonHolder {  
                private static final LazyInitHolderSingleton INSTANCE = new LazyInitHolderSingleton();  
        }  

        public static LazyInitHolderSingleton getInstance() {  
                return SingletonHolder.INSTANCE;  
        }  
}  

列舉式

public enum SingletonClass
{
    INSTANCE;
}

Jdk中的單例模式:
java.lang.Runtime#getRuntime()

總結

列舉式式最簡單最優秀的單例寫法,可以防止反射工具(詳細參考《如何防止單例模式被JAVA反射攻擊》)和序列化破壞(詳細參考《JAVA序列化 》),《Effective Java》的作者Joshua Bloch推薦使用這種寫法,博主也認為這種寫法不錯,只是用的人較少,沒有普遍性,建議程式設計時採用佔位符式(不能防止反射和序列化破壞),當然寫成列舉式就更好啦。

歡迎支援《RabbitMQ實戰指南》以及關注微信公眾號:朱小廝的部落格。