設計模式(一)——單例模式

單例模式

​ 確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。構造器私有化,不能被new出來。java

項目代碼:Githubgit

單例的應用

優勢

  1. 在內存中只有一個實例, 減小了內存開支
  2. 當一個對象的產生須要 比較多的資源時, 如讀取配置、 產生其餘依賴對象時, 則能夠經過在應用啓動時直接產生一 個單例對象, 而後用永久駐留內存的方式來解決
  3. 單例模式能夠避免對資源的多重佔用, 例如一個寫文件動做, 因爲只有一個實例存在 內存中, 避免對同一個資源文件的同時寫操做。
  4. 單例模式能夠避免對資源的多重佔用, 例如一個寫文件動做, 因爲只有一個實例存在 內存中, 避免對同一個資源文件的同時寫操做。

缺點

  1. 單例模式通常沒有接口, 擴展很困難;
  2. 單例模式對測試是不利的。 在並行開發環境中, 若是單例模式沒有完成, 是不能進行 測試的, 沒有接口也不能使用mock的方式虛擬一個對象。
  3. 單例模式與單一職責原則有衝突。

幾種單例實現:github

一、餓漢模式

public class SingleDemo {
    private static SingleDemo instance = new SingleDemo();
    //私有化構造器
    private SingleDemo() {
        //防止其餘經過反射調用構造方法,破解單例
        if (instance != null) {
            throw new RuntimeException();
        }
    }
    //對外提供統一的訪問點
    public static SingleDemo getInstance() {
        return instance;
    }
}
複製代碼

優勢:安全

  • 實例的初始化由JVM裝載類的時候進行,保證了線程的安全性
  • 實現簡單方便,訪問效率高

缺點:微信

  • 不能實現懶加載,資源的利用率不高

二、懶漢模式

public class SingleDemo2 {
    // 此處並不初始化實例
    private static SingleDemo2 instance;

    private SingleDemo2() {
        if (instance != null) {
            throw new RuntimeException();
        }
    }
    /** * 當調用此方法的時候才初始化實例, 爲了實現線程安全,須要使用同步方法 */
    public static synchronized SingleDemo2 getInstance() {
        if (instance == null) {
            instance = new SingleDemo2();
        }
        return instance;
    }
}
複製代碼

優勢:dom

  • 只有使用這個類的時候才初始化實例,優化了資源利用率

缺點:性能

  • 爲了實現線程安全,使用了同步方法獲取,增長了訪問的開銷

三、雙重檢查

public class SingleDemo3 {
    private static SingleDemo3 instance;

    private SingleDemo3() {
        if (instance != null) {
            throw new RuntimeException();
        }
    }

    public static SingleDemo3 getInstance() {
        //第一重檢查,提升效率
        if (instance == null) {
            synchronized (SingleDemo3.class) {
                //第二重檢查保證線程安全
                if (instance == null) {
                    instance = new SingleDemo3();
                }
            }
        }
        return instance;
    }
}
複製代碼

優勢:測試

  • 實現懶加載
  • 經過縮小同步區域和第一次檢查提升訪問效率

缺點:優化

  • 爲了實現線程安全,使用了同步方法獲取,增長了訪問的開銷

四、靜態內部類

public class SingleDemo4 {
    private static SingleDemo4 instance;

    private static class SingleDemo4Holder {
        private static final SingleDemo4 instance = new SingleDemo4();
    }

    private SingleDemo4() {
        if (instance != null) {
            throw new RuntimeException();
        }
    }

    /** * 調用這個方法的時候,JVM才加載靜態內部類,才初始化靜態內部類的類變量。因爲由JVM初始化,保證了線程安全性, * 同時又實現了懶加載 */
    public static SingleDemo4 getInstance() {
        return SingleDemo4Holder.instance;
    }
}
複製代碼

優勢:spa

  • 即實現了線程安全,又實現了懶加載

缺點:

  • 實現稍顯複雜

五、枚舉類

public enum SingleDemo5 {
     INSTANCE;
    public void someMethod(){
        
    }
}
複製代碼

優勢:

  • 實現簡單
  • 線程安全

缺點:

  • 不能實現懶加載

結論

若是須要懶加載就使用靜態內部類方式,若是不須要就使用枚舉方式

單例模式的擴展

若是要求一個類只能生產固定數量的實例。

public class SingleDemo6{
    // 最多能夠生成的單例數量
    private static int maxNumberSingleDemo = 2;
    // 定義列表存放實例
    private static List<SingleDemo6> singleDemoList = new ArrayList<>();
    
    //生成對象
    static{
        for(int i=0; i<maxNumberSingleDemo; i++){
            singleDemoList.add(new SingleDemo6());
        }
    }
    
    private SingleDemo6(){}
    
    public static SingleDemo6 getInstance(){
        Random random = new Random();
        //隨機調用一個實例
        int number = random.nextInt(maxNumberSingleDemo);
        return singleDemoList.get(number);
    }
    
}
複製代碼

這種須要產生固定數量對象的模式就叫作有上限的多例模式, 它是單例模式的一種擴展, 採用有上限的多例模式, 咱們能夠在設計時決定在內存中有多少個實例, 方便系統進行 擴展, 修正單例可能存在的性能問題, 提供系統的響應速度。 例如讀取文件, 咱們能夠在系 統啓動時完成初始化工做, 在內存中啓動固定數量的reader實例, 而後在須要讀取文件時就 能夠快速響應。


歡迎關注公衆號:

公衆號微信
相關文章
相關標籤/搜索