單例模式分爲「餓漢模式」與「懶漢模式」。今天咱們來聊聊單例模式,特別是在多線程中咱們須要特別注意。安全
class Singleton{ private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } }
這種模式稱之爲單例模式的餓漢模式。該模式是線程安全的。多線程
class LazySingleton{ private static LazySingleton lazySingleton; private LazySingleton(){} public static LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; } }
這種方式爲單例模式的懶漢模式,只有在第一次調用的時候纔會去初始化對象。這種狀況在多線程狀況下會出現非線程安全問題。那麼咱們就來解決懶漢模式的線程安全問題。ide
class LazySingleton{ private static LazySingleton lazySingleton; private LazySingleton(){} synchronized public static LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; } }
使用synchronized關鍵字把get方法設置爲同步方法,能夠解決線程安全問題,可是效率低是一個問題,那麼咱們考慮優化一下這個效率,咱們能夠使用同步代碼塊,由於咱們只是在初始化處會出現線程安全問題。優化
class LazySingleton{ private static LazySingleton lazySingleton; private LazySingleton(){} public static LazySingleton getInstance(){ synchronized(LazySingleton.class) { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } } return lazySingleton; } }
同步代碼塊也能夠解決線程安全問題,可是上面的方式其實跟同步方法效率上沒有什麼很大不一樣。咱們關鍵的同步代碼實際上是lazySingleton = new LazySingleton();只要給它加上同步標識就能夠,可是這樣會有一個問題就是多個線程判斷了須要初始化該類實例,但只能有一個操做,後面的阻塞,這個時候一但前面線程實例化完後,後面的線程又會實例化,因此咱們須要進行再次的檢查,判斷是否已經實例化了,這就是DCL(雙重檢查)實現更細粒度的同步代碼塊。線程
class LazySingleton{ private static LazySingleton lazySingleton; private LazySingleton(){} public static LazySingleton getInstance(){ if(lazySingleton == null) { synchronized (LazySingleton.class) { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } } } return lazySingleton; } }
那麼說到這裏就把單例模式介紹完了,上述問題中若是有什麼錯誤的地方,請留言告訴我,我會第一時間修改,以防止誤導讀者。謝謝!code