面試的時候,經常會被問到這樣一個問題:請您寫出一個單例模式(Singleton Pattern)吧。好吧,寫就寫,這還不容易。順手寫一個: java
public final class EagerSingleton { private static EagerSingleton singObj = new EagerSingleton(); private EagerSingleton(){ } public static EagerSingleton getSingleInstance(){ return singObj; } }
這種寫法就是所謂的飢餓模式,每一個對象在沒有使用以前就已經初始化了。這就可能帶來潛在的性能問題:若是這個對象很大呢?沒有使用這個對象以前,就把它加載到了內存中去是一種巨大的浪費。針對這種狀況,咱們能夠對以上的代碼進行改進,使用一種新的設計思想——延遲加載(Lazy-load Singleton)。 面試
public final class LazySingleton { private static LazySingleton singObj = null; private LazySingleton(){ } public static LazySingleton getSingleInstance(){ if(null == singObj ) singObj = new LazySingleton(); return singObj; } }
這種寫法就是所謂的懶漢模式。它使用了延遲加載來保 證對象在沒有使用以前,是不會進行初始化的。可是,一般這個時候面試官又會提問新的問題來刁難一下。他會問:這種寫法線程安全嗎?回答必然是:不安全。這 是由於在多個線程可能同時運行到第九行,判斷singObj爲null,因而同時進行了初始化。因此,這是面臨的問題是如何使得這個代碼線程安全?很簡 單,在那個方法前面加一個Synchronized就OK了。 安全
public final class ThreadSafeSingleton { private static ThreadSafeSingleton singObj = null; private ThreadSafeSingleton(){ } public static Synchronized ThreadSafeSingleton getSingleInstance(){ if(null == singObj ) singObj = new ThreadSafeSingleton(); return singObj; } }
寫到這裏,面試官可能仍然會狡猾的看了你一眼,繼續刁難到:這個寫法有沒有什麼性能問題呢?答案確定是有的!同步的代價必然會必定程度的使程序的併發度下降。那麼有沒有什麼方法,一方面是線程安全的,有能夠有很高的併發度呢?咱們觀察到,線程不安全的緣由實際上是在初始化對象的時候,因此,能夠想辦法把同步的粒度下降,只在初始化對象的時候進行同步。這裏有必要提出一種新的設計思想——雙重檢查鎖(Double-Checked Lock)。 併發
public final class DoubleCheckedSingleton { private static DoubleCheckedSingletonsingObj = null; private DoubleCheckedSingleton(){ } public static DoubleCheckedSingleton getSingleInstance(){ if(null == singObj ) { Synchronized(DoubleCheckedSingleton.class){ if(null == singObj) singObj = new DoubleCheckedSingleton(); } } return singObj; } }
那麼有沒有什麼更好的寫法呢?有!這裏又要提出一種新的模式——Initialization on Demand Holder. 這種方法使用內部類來作到延遲加載對象,在初始化這個內部類的時候,JLS(Java Language Sepcification)會保證這個類的線程安全。這種寫法最大的美在於,徹底使用了Java虛擬機的機制進行同步保證,沒有一個同步的關鍵字。 性能
public class Singleton { private static class SingletonHolder { public final static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }