引用維基百科對單例的說明:html
單例模式,也叫單子模式,是一種經常使用的軟件設計模式。在應用這個模式時,單例對象的類必須保證只有一個實例存在。java
繼續引用維基百科的實現思路:設計模式
實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個得到該實例的方法(必須是靜態方法,一般使用getInstance這個名稱);當咱們調用這個方法時,若是類持有的引用不爲空就返回這個引用,若是類保持的引用爲空就建立該類的實例並將實例的引用賦予該類保持的引用;同時咱們還將該類的構造函數定義爲私有方法,這樣其餘處的代碼就沒法經過調用該類的構造函數來實例化該類的對象,只有經過該類提供的靜態方法來獲得該類的惟一實例。安全
單例的思路就這麼簡單,不像其餘的設計模式那樣通常有幾個類。它只有一個類。多線程
在討論 Java 的單例模式時,猿們通常會提到二者實現方式:懶漢式 和 餓漢式 。爲何會有這兩種叫法?看了實現代碼後,相信您會突然大悟。併發
餓漢式,我是餓漢,不能等,建立(類)時就要給我吃的(實例化對象),否則我會餓死:函數
public class Singleton{ //建立類時就實例化出單例。 private final static Singleton INSTANCE = new Singleton(); //使用私有的構造器來阻止外部(其餘代碼)實例化該對象 private Singleton(){} //因爲該類的構造方法是私有的,只能在該類內部實例化對象。所以必須提供一個靜態的公有方法做爲出口,提供單例。 public static Singleton getInstance(){ return INSTANCE; } }
懶漢式, 我是懶漢,你叫我幹活(給 INSTANCE new 一個單例)才幹,懶是個人天性,這不能怪我:.net
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.lang.Runtime 使用的單例(餓漢式,不涉及到線程安全):
public class Runtime{ private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime(){ return currentRuntime; } private Runtime(){} //... 其餘方法 }
維基百科
StackExchange
使用枚舉實現單例 Effective Java 第三條建議