在應用程序中,常常會用到單例模式,即這個類只能存在一個對象實例。
那麼爲何須要這種模式,咱們在一個程序應用中,只須要建立一次性的對象實例以節省內存資源,避免重複建立的開銷,以便後面使用能夠更快的訪問。java
單例做爲全部設計模式中最簡單的設計模式之一,其建立是很是簡單的。設計模式
#code 餓漢式單例-不推薦 public final class HungrySingleton { private byte[] data = new byte[1024]; private static HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
以上就是一個典型的餓漢式單例模式,在咱們調用HungrySingleton. getInstance()方法時,不單單獲取了一個已經建立的對象,而且獲取了已經初始化了的1k的類變量數據。
即使在多線程環境下,instance也不會被建立兩次,由於在類初始化的時候被收集進
可是這個有一個缺陷,instance在被類加載以後,很長一段時間都會被常駐在堆內存,若是這個一個輕量級的類,那卻是無所謂,但若是這個類比較重,那麼這種建立方式就不太穩當。
#code 懶漢式單例-不推薦 public final class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } public static synchronized LazySingleton getInstance() { if (null == instance) { instance = new LazySingleton(); } return instance; } }
上面這一段代碼其實就是懶漢式單例,咱們在真正調用getInstance()方法的時候纔去建立這個實例,這個類所需的資源到這個時候纔會被開闢。咱們同時也使用synchronized來保證多線程環境下只有一份實例。
看起來很美妙,但惋惜的是,這個方式也一樣不被推薦。緣由也很簡單,由於咱們在使用getInstance()的時候是同步的,意味着每一個調用該方法的線程都必須阻塞等待其餘線程調用完成,這一點就很耗費性能。多線程
#code 雙重檢查式單例-不推薦 public final class DCSingleton { private static volatile DCSingleton instance; private DCSingleton() { } public static DCSingleton getInstance(){ if (null == instance){ synchronized (DCSingleton.class){ if (null == instance){ instance = new DCSingleton(); } } } return instance; } }
在我很長的一段時間內以來,在建立單例實例的時候都喜歡使用這種方式。它既保證了線程安全,也保證了延遲加載,同時相比懶漢式單例的耗費性能,它使用的雙重檢查的技巧很大程度上緩解了性能浪費,並且volatile修飾的instance也不會被指令重排困擾。看上去很完美,從必定程度上來講的確是這樣。
直到我看了doug lea與人合著的那本併發實踐書籍,原文是這樣的:「DCL這種使用方法已經被普遍的廢棄了——促使該模式出現的驅動力不復存在,於是它不是一種高效的優化措施。延遲初始化佔位類模式能帶來一樣的優點,而且更容易理解。」這裏的驅動力是指,在新的版本下,無競爭同步的執行速度變快了(之前很慢),JVM的啓動速度也變快了(之前很慢)。併發
#code Holder式單例-推薦max public final class HolderSingleton { private HolderSingleton() { } private static class Holder { private static HolderSingleton instance = new HolderSingleton(); } public static HolderSingleton getInstance() { return Holder.instance; } }
從線程安全、高性能、懶加載來看,這個應該是目前最好的單例設計之一,也是使用最爲普遍的一個。性能
#code Holder式單例-酌情使用 public enum EnumSingleton { INSTANCE; EnumSingleton() { System.out.println("complete init..."); } public static EnumSingleton getInstance() { System.out.println("getInstance..."); return INSTANCE; } public static void doneTask() { System.out.println("doneTask..."); } }
在《Effective Java》那本書中,這個枚舉方式實現單例的方式就被極爲推薦。枚舉方式不容許被繼承,一樣也只是被實例化一次,可是不可以懶加載。因此讀者能夠酌情使用。優化