单例模式(Singleton)小记
概念
引用維基百科對單例的說明:
單例模式,也叫單子模式,是一種常用的軟件設計模式。在應用這個模式時,單例對象的類必須保證只有一個實例存在。
繼續引用維基百科的實現思路:
實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就創建該類的實例并將實例的引用賦予該類保持的引用;同時我們還將該類的構造函數定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。
單例的思路就這么簡單,不像其他的設計模式那樣一般有幾個類。它只有一個類。
Java 中的單例實現
懶漢式與餓漢式
在討論 Java 的單例模式時,猿們一般會提到兩者實現方式:懶漢式 和 餓漢式 。為什么會有這兩種叫法?看了實現代碼后,相信您會忽然大悟。
餓漢式,我是餓漢,不能等,創建(類)時就要給我吃的(實例化對象),不然我會餓死:
public class Singleton{//創建類時就實例化出單例。private final static Singleton INSTANCE = new Singleton();//使用私有的構造器來阻止外部(其他代碼)實例化該對象private Singleton(){}//由于該類的構造方法是私有的,只能在該類內部實例化對象。因此必須提供一個靜態的公有方法作為出口,提供單例。public static Singleton getInstance(){return INSTANCE;} }懶漢式, 我是懶漢,你叫我干活(給 INSTANCE new 一個單例)才干,懶是我的天性,這不能怪我:
public class Singleton{private static Singleton INSTANCE;private Singleton(){}public static Singleton getInstance(){if(INSTANCE == null){INSTANCE = new Singleton();}return INSTANCE;} }OK,單例模式就是這么簡單,結束了。呵呵,真的結束了嗎?圖樣圖森破!
上面的兩個實現確實是實現了漢式和懶漢式。而且它們在單線程下能很好地運行。但如果我們把它們應用在多線程下呢?試一下就知道,餓漢式依然堅挺,瀟灑應對。但懶漢式就瞬間爆炸了!沒辦法誰叫你懶,出來混總是要還的。即使是代碼,懶也要付出代價~~開玩笑的^_^,爆炸的原因肯定不是懶的原因啦,是線程同步的問題,造成了非線程安全的懶漢式。
既然上面的懶漢式在多線程下爆炸了,我們就要去拯救它,總不能見死不救吧~~
多線程下懶漢式的自我拯救
知道懶漢式爆炸的原因時線程同步的問題,我們最簡單的拯救方法就是直接進行同步加鎖,創建線程安全的懶漢式:
public class Singleton{private static Singleton INSTANCE;private Singleton(){}public static synchronized Singleton getInstance(){if(INSTANCE == null){INSTANCE = new Singleton();}return INSTANCE;} }通過簡單的對 getInstance 方法進行加鎖就可以把非線程安全的懶漢式改為線程安全的,簡單吧!但,正如天下沒免費的午餐一樣,如此簡單的拯救方法肯定是要付出代價的。因此這種實現方法會降低效率。
好吧,效率低下,我改還不行。經過改改改后,我們又得出了另外一種線程安全懶漢式單例:雙重校驗鎖(Double-Checked Locking)
public class Singleton{private static volatile Singleton INSTANCE;private Singleton(){}public static Singleton getInstance(){if(INSTANCE == null){synchronized(Singleton.class){if(INSTANCE == null){INSTANCE = new Singleton();}}}return INSTANCE;} }該方法比之前的線程安全懶漢式效率高的原因是,之前的實現方法每次獲取單例是都要進行同步,每一次只能有一個線程進入 getInstance 方法獲取實例;而該方法把同步塊縮小了,而且只在 INSTANCE 為 null 時才會進行同步訪問(也就是說只需要一次同步)。之后都不需要同步,可以并發地獲取實例。
很多人都知道,JDK 1.5 之前利用這種方法實現是有問題的。但在 JDK 1.5 后,Java 的內存已經修改了,該方法在 1.5 后能正常運行,可以放心使用。
其他的實現方法
使用靜態內部類實現
public class Singleton{private static class SingletonHolder{private static final Singleton INSTANCE = new Singleton();}private Singleton(){}public static final Singleton getInstance(){return SingletonHolder.INSTANCE;} }該方法利用了靜態內部類的特性實現了類似與懶漢式的單例模式
(單元素的)枚舉實現
public enum Singleton{INSTANCE; }枚舉實現的單例已經簡單到不能再簡單了,可以直接使用 Singleton.INSTANCE 來獲取單例。它雖然簡單,但也是線程安全的實現方法,并且也是可序列化的。在 Effective Java 第二版里也推薦使用這種方法: 單元素的枚舉類型已經成為實現 Singleton 的最佳方法
單例在 Java 中的應用
- Java.awt.Toolkit with getDefaultToolkit()
- Java.awt.Desktop with getDesktop()
- java.lang.Runtime
java.lang.Runtime 使用的單例(餓漢式,不涉及到線程安全):
public class Runtime{private static Runtime currentRuntime = new Runtime();public static Runtime getRuntime(){return currentRuntime;}private Runtime(){}//... 其他方法 }參考
維基百科
StackExchange
使用枚舉實現單例
Effective Java 第三條建議
轉載于:https://www.cnblogs.com/averey/p/4499358.html
總結
以上是生活随笔為你收集整理的单例模式(Singleton)小记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AES-256 加密 PHP实现
- 下一篇: 系统级alias vs Oracle A