Java的單例模式

單例模式是最多見的設計模式,也是面試必問的(基本)。你真的會寫一個正確的高效的單例嗎?下面分幾個階段介紹寫法,直接上代碼面試

01

public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo() { }/** * 原始 * * @return
     */
    public static SingletonDemo getInstance() { if (instance == null) { instance = new SingletonDemo(); } return instance; } }

以上寫法在高併發狀況下會建立多個對象實例。設計模式

02

若是存在併發問題,那首先想到的是加鎖。併發

public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo() { }/** * 方法同步鎖 * * @return
     */
    public synchronized static SingletonDemo getInstance() { if (instance == null) { instance = new SingletonDemo(); } return instance; } }

以上寫法能夠避免併發問題。可是效率很差,緣由:在高併發環境中,若是此類已建立對象,後來的併發請求也要爭搶鎖後才能獲取對象實例,最好建立了對象直接返回就好。函數

03

關於效率問題,我想到用此方式解決。方法不加synchronized關鍵字,在建立對象的代碼處加synchronized高併發

public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo() { }/** * 建立對象時同步鎖 * * @return
     */
    public static SingletonDemo getInstance() { if (instance == null) { //[1]
            synchronized (SingletonDemo.class) { instance = new SingletonDemo(); } } return instance; } }

以上寫法,在成員變量上加了 volatile關鍵字,目的是保證線程間的可見性,及禁止指令重排序。優化

但仍是有問題,[1]處,2個線程同時運行到1處,線程-1拿到鎖並建立成功返回,並釋放鎖,線程-2拿到鎖又會建立個新的對象實例,這樣就破壞了單例。spa

04

終極寫法(大佬有更好寫法請指教 哈哈)線程

public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo() { } /** * 終極
   *  *
@return */ public static SingletonDemo getInstance() { if (instance == null) { synchronized (SingletonDemo.class) { if (instance == null) { instance = new SingletonDemo(); } } } return instance; } }

這樣就完美解決了 03 的問題。謝謝~設計

關於volatile關鍵字:code

  解決可見性:線程改變變量值後會當即寫入主存,其餘線程當即可見。

  禁止重排序:在 Singleton 構造函數體執行以前,變量 instance 可能成爲非 null 的,即賦值語句在對象實例化以前調用(重排序 JIT編譯器優化),此時別的線程獲得的是一個還會初始化的對象,這樣會致使系統崩潰。

參考:

https://www.iteye.com/topic/652440

相關文章
相關標籤/搜索