微信搜索:碼農StayUp
主頁地址:https://gozhuyinglong.github.io
源碼分享:https://github.com/gozhuyinglong/blog-demosjava
單例模式(Singleton Pattern)是一種簡單的對象建立型模式。該模式保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。git
因此要實現單例模式,要作到如下幾點:github
對於單例模式有如下5種實現。安全
該方式是使用synchronized
關鍵字進行加鎖,保證了線程安全性。
優勢:在第一次調用才初始化,避免了內存浪費。
缺點:對獲取實例方法加鎖,大大下降了併發效率。微信
因爲加了鎖,對性能影響較大,不推薦使用。多線程
public class SingletonLazy { /** * 私有實例 */ private static SingletonLazy instance; /** * 私有構造方法 */ private SingletonLazy() { } /** * 惟一公開獲取實例的方法(靜態工廠方法),該方法使用synchronized加鎖,來保證線程安全性 * * @return */ public static synchronized SingletonLazy getInstance() { if (instance == null) { instance = new SingletonLazy(); } return instance; } }
餓漢式是利用類加載機制來避免了多線程的同步問題,因此是線程安全的。
優勢:未加鎖,執行效率高。
缺點:類加載時就初始化實例,形成內存浪費。併發
若是對內存要求不高的狀況,仍是比較推薦使用這種方式。性能
public class SingletonEager { /** * 私有實例,靜態變量會在類加載的時候初始化,是線程安全的 */ private static final SingletonEager instance = new SingletonEager(); /** * 私有構造方法 */ private SingletonEager() { } /** * 惟一公開獲取實例的方法(靜態工廠方法) * * @return */ public static SingletonEager getInstance() { return instance; } }
利用了volatile修飾符的線程可見性(被一個線程修改後,其餘線程當即可見),即保證了懶加載,又保證了高性能,因此推薦使用。編碼
public class SingletonDCL { /** * 私有實例,volatile修飾的變量是具備可見性的(即被一個線程修改後,其餘線程當即可見) */ private volatile static SingletonDCL instance; /** * 私有構造方法 */ private SingletonDCL() { } /** * 惟一公開獲取實例的方法(靜態工廠方法) * * @return */ public static SingletonDCL getInstance() { if (instance == null) { synchronized (SingletonDCL.class) { if (instance == null) { instance = new SingletonDCL(); } } } return instance; } }
該模式利用了靜態內部類延遲初始化的特性,來達到與雙重校驗鎖方式同樣的功能。因爲須要藉助輔助類,並不經常使用。線程
public class SingletonInnerClass { /** * 私有構造方法 */ private SingletonInnerClass() { } /** * 惟一公開獲取實例的方法(靜態工廠方法) * * @return */ public static SingletonInnerClass getInstance() { return LazyHolder.INSTANCE; } /** * 私有靜態內部類 */ private static class LazyHolder { private static final SingletonInnerClass INSTANCE = new SingletonInnerClass(); } }
該方式利用了枚舉類的特性,不只能避免線程同步問題,還防止反序列化從新建立新的對象。這種方式是 Effective Java 做者 Josh Bloch 提倡的方式。
但因爲這種編碼方式還不能適應,因此實際工做中不多使用。
public enum SingletonEnum { INSTANCE; public void method() { System.out.println("枚舉類中定義方法!"); } }