Java 中單例模式是一種常見的設計模式,幾乎 設計模式面試題中必問的,開始以前咱們先了解一下爲何須要單例模式。java
爲何須要單例模式面試
單例模式確保某個類只有一個實例,並且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具備資源管理器的功能。每臺計算機能夠有若干個打印機,但只能有一個Printer Spooler,以免兩個打印做業同時輸出到打印機中。每臺計算機能夠有若干通訊端口,系統應當集中管理這些通訊端口,以免一個通訊端口同時被兩個請求同時調用。總之,選擇單例模式就是爲了不不一致狀態。設計模式
單例模式特色緩存
單例類只能有一個實例;安全
單例類必須本身建立本身的惟一實例;多線程
單例類必須給全部其餘對象提供這一實例。this
單例模式的寫法有好幾種,這裏介紹經常使用的幾種單例模式:懶漢式單例、餓漢式單例、內部類式、枚舉式、雙重校驗式單例。spa
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
寫法評價:線程
優勢:延遲加載很明顯;設計
缺點:致命缺點的是線程不安全,在多線程不能正常工做。
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
寫法評價:
優勢:線程安全,多線程中很好的工做,並且看起來它也具有很好的延遲加載;
缺點:效率很低,實際99%狀況下不須要同步。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
寫法評價:
優勢:基於classloder 機制避免了多線程的同步問題,線程安全;
缺點:初始化 instance 顯然沒有達到延遲加載的效果。緣由是致使類裝載的狀況有不少種,在單例模式中咱們指望的是調用 getInstance 方法初始化 instance, 可是咱們不能肯定有其餘的方式也致使類的裝載如調用其餘的靜態方法。
public class Singleton { private Singleton instance = null; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return this.instance; } }
寫法評價:
優勢:使用靜態代碼塊初始化實例,線程安全;
缺點:加載時機其實和第三種方式差很少,都是在類初始化即實例化instance, 效率比較低。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
寫法評價:
線程安全,利用了classloder的機制來保證初始化instance時只有一個線程;延遲加載,只有顯示經過調用 getInstance 方法時,纔會顯示裝載 SingletonHolder 類,再回初始化 Singleton 類。(第三種和第四種方式是隻要Singleton類被裝載了,那麼instance就會被實例化)
public enum Singleton { INSTANCE; public void whateverMethod() { } }
寫法評價:
這種方式是 Effective Java 做者 Josh Bloch 提倡的方式,它不只能避免多線程同步問題,並且還能防止反序列化從新建立新的對象,可謂是很堅強的壁壘啊,不過,我的認爲因爲1.5中才加入 enum 特性,用這種方式寫難免讓人感受生疏,在實際工做中,我也不多看見有人這麼寫過。
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
寫法評價:
使用 volatile 保證 instance 在線程間的可見性,在調用 getInstance 實例化時,使用 synchronized 鎖住類模板,保證線程安全,在 synchronized 代碼塊中使用雙重校驗機制保證類只會被實例化一次,達到線程安全和延遲加載的特性。