單例模式

背景:單例模式模式是在編程中常常使用,他能夠對須要使用的資金進行一次性初始化,防止屢次初始化屢次資源釋放帶來性能的開銷。編程

最近在讀《JAVA併發編程的藝術》發現有些知識點不錯,整理出來。緩存

單例模式經常使用模式是懶漢模式和餓漢模式多線程

懶漢模式:就是用到時候才new出來。併發

餓漢模式:類一開始就加載好,可直接使用。性能

 

單線程狀況下,咱們會經過如下實現才生成一個懶漢模式的單例模式類。可是多線程訪問時候,這種實現不知足要求。優化

public class Singleton {
    public static Singleton instance = null;

    public Singleton() {
        //todo
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

上面實現,若是在多線程狀況下,可能出現多個線程同時訪問instance == null 出現問題。接下來,介紹幾種能夠避免多線程競爭訪問致使的問題。spa

(一)同步處理,延遲初始化,任什麼時候刻只有一個線程搶佔到訪問資源。線程

public class Singleton {
    public static Singleton instance = null;

    public Singleton() {
        //todo
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

可是這個方法也是弊端,因爲採用同步處理,synchronized致使性能開銷。若是getInstance()被多個線程頻繁調用,會致使程序執行性能降低。反之,若是getInstance()不被多個線程頻繁調用,那麼這個延遲初始化將是不錯的選擇。code

(二)雙重檢查鎖,優化方案一出現多線程頻繁調用instance(instance!=null)時阻塞的問題。對象

public class Singleton {
    public static Singleton instance = null;

    public Singleton() {
        //todo
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

優勢:若是instance不爲null, 多線程訪問instance就不會出現阻塞,提升了性能,減小了開銷。

缺點:因爲JAVA內存模型,不一樣JIT編譯器之間出現重排序問題,可能會出現線程A執行instance時instance==null,因而進入instance = new Singleton(), 線程B發現instance != null,可是這個instance可能未初始化。接下來我來細化下JMM執行過程方便理解。

instance = new Singleton() 能夠分解成如下幾步:

1. memory = allocate(); 分配對象的內存空間

2. ctorInstance(memory); 初始化對象

3. instance = memory; 設置instance指向剛分配的內存地址

因爲在爲僞代碼中2和3之間可能會出現重排序. 引入《JAVA併發編程的藝術》的圖。懶得本身畫了。

鑑於這個問題,咱們能夠有以下更佳解決方式,把instance定義爲volatile。JDK5以後引入volatile關鍵字,解決各個編譯器處理器緩存和總線內存之間數據一致性問題。當把對象聲明爲volatile後,2和3重排序問題的問題,在多進程中被禁止,即不容許重排序。

public class Singleton {
    public static volatile Singleton instance = null;

    public Singleton() {
        //todo
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

(三)若是你使用Spring來管理bean, @Autowired註釋自己就是單例模式,一開始就生成好一個bean實例,即爲餓漢模式。

@Component
public class Singleton {
    
    @Autowired
    public Singleton(....){
        //TODO
    }
}
相關文章
相關標籤/搜索