單例模式及其線程安全問題

單例模式

餓漢模式(飢渴,一開始就實例化對象)

package java_study;

public class HungerySingletonDemo {
    //1.私有化構造器
    private HungerySingletonDemo() {}
    //2.實例化對象(餓漢)
    private static HungerySingletonDemo instance = new HungerySingletonDemo();
    //3.提供獲取方法
    public static HungerySingletonDemo getInstance() {
        return instance;
    }
}

懶漢模式(也叫懶加載)(直到被調用才實例化對象)

package java_study;

public class LazySingletonDemo {
    // 1.私有化構造器
    private LazySingletonDemo() {
    }

    // 2.聲明對象(餓漢)
    private static LazySingletonDemo instance = null;

    // 3.提供獲取方法並實例化,使用判斷是否單例
    public static LazySingletonDemo getInstance() {
        if (instance == null) {
            instance = new LazySingletonDemo();
        }
        return instance;
    }
}

線程安全問題

因爲懶漢式存在多個線程調用同一個對象資源,存在線程不安全問題,須要使用同步機制來解決該問題
餓漢模式不存在線程安全問題,在加載類的時候就實例化對象了java

同步方法

// 3.提供獲取方法並實例化,使用判斷是否單例
synchronized public static LazySingletonDemo getInstance() {
    if (instance == null) {
        instance = new LazySingletonDemo();
    }
    return instance;
}

能解決線程安全問題,但因爲synchronized的做用域過大,應當使用雙重檢查加鎖緩存

同步代碼塊(雙重檢查加鎖)

雙重檢查加鎖,進入getInstance方法先不一樣步,先判斷實例是否存在,若是不存在執行一次同步代碼塊,之後再也不執行保證了安全性,又不會使性能受過大的影響安全

// 2.聲明對象(餓漢),使用volatile 該資源不會被本地線程緩存,資源直接共享內存,保證了多個線程能正確處理該資源
private static volatile LazySingletonDemo instance = null;

// 3.提供獲取方法並實例化,使用判斷是否單例
public static LazySingletonDemo getInstance() {
    if (instance == null) {
        //多個線程有可能同時進入這個位置,仍然會建立多個實例,若是線程存在,之後都不會執行同步代碼塊了
        synchronized (LazySingletonDemo.class) {//因爲該方法是靜態方法,同步鎖是 類.class
            //所以須要再次判斷
            if (instance == null) {
                instance = new LazySingletonDemo();
            }
        }
    }
    return instance;
}

!推薦使用餓漢模式,簡單粗暴

相關文章
相關標籤/搜索