二學單例模式

二學單例模式

哎呀!單例都學爛了,有什麼好學的啊?

因爲今天看《Head First 設計模式》看到了一些細節部分,決定重新認識一下單例

應用場景

  • 線程池
  • 緩存
  • 註冊表
  • 工廠
  • 對話框
  • 偏好設置

普通的單例

我們先來看看

public Single{
    private static Single singleInstance;
	private Single(){}
    public static Single getSingleInstance(){
        if(singleInstance == null){
            singleInstance = new Single();
        }
        return singleInstance;
    }
}

很簡單的一個例子,大家都會寫,把構造隱藏起來,把方法暴露出去,blablabla…

但是這涉及到多線程的話,如果我兩個線程同時到達if(singleInstance == null),怎麼辦,這就會出來兩個不同的對象啊,這是不安全的,那怎麼辦呢?

線程安全性

處理多線程問題,我們有三種方法

方法一:提前new好

別嫌代碼臭嗷~

public Single{
    private static Single singleInstance = new Single();
	private Single(){}
    public static Single getSingleInstance(){
        return singleInstance;
    }
}

JVM加載這個類時,馬上就會創建好這個對象。如果在創建和運行方面壓力不大,你又急着用,確實可以這麼做

方法二:增加同步(synchronized)塊

還記得synchronized嗎?

public Single{
    private static Single singleInstance;
	private Single(){}
    public static synchronized Single getSingleInstance(){
        if(singleInstance == null){
            singleInstance = new Single();
        }
        return singleInstance;
    }
}

在方法中間加上synchronized關鍵字,問題解決了,但如果我對象都已經創建好了了,我直接拿不久行了,也擱哪兒等着?所以,隨之而來的是性能的災難…(可能差幾十倍甚至一百倍)

方法三:減少同步,使用雙重鎖DCL(Double-Checked Lock)

先檢查單例對象創建了沒,沒創建再同步,而不是不管三七二十一,全同步了

public Single{
    private volatile static Single singleInstance;
	private Single(){}
    public static Single getSingleInstance(){
        if(singleInstance == null){
            synchronized(Single.class){
                if(singleInstance == null){
                    singleInstance = new Single();
                }
            }          
        }
        return singleInstance;
    }
}

利用雙重鎖機制,性能🆙

單例 vs 全局變量

還記得我“二學static”裏面的例子嗎?

//Code.1
public Single{
    static public void FuncA(Object o);
    static public void FuncB(Object o);
    static public void FuncC(Object o);
}

// Code.2
public Single{
    private static Single singleInstance;
	private Single(){}
    public static Single getSingleInstance(){
        if(singleInstance == null){
            singleInstance = new Single();
        }
        return singleInstance;
    }
    public void FuncA(Object o);
    public void FuncB(Object o);
    public void FuncC(Object o);
}

// 分別調用FuncA
Object o = new Object();
// call 1
Single.FuncA(o);
// call 2
Single.getSingleInstance().FuncA(o);

除了我之前文章說的不同,還存在初始化的和控制權不同的問題

初始化

  • 單例延遲初始化
  • 類加載後的初始化時間在Java程序

控制權

  • 單例控制權在我們
  • 全局變量控制權在Java
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章