建立型模式-單例模式(確保對象惟一性)

1. 定義

單例模式(Singleton Pattern):確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。單例模式是一種對象建立型模式。git

2. 結構

單例模式有三個要點:一是某個類只能有一個實例;二是它必須自行建立這個實例;三是它必須自行向整個系統提供這個實例。單例模式是結構最簡單的設計模式一,在它的核心結構中只包含一個被稱爲單例類的特殊類。單例模式結構如圖所示:設計模式

單例模式

  • Singleton(單例):在單例類的內部實現只生成一個實例,同時它提供一個靜態的getInstance()工廠方法,讓客戶能夠訪問它的惟一實例;爲了防止在外部對其實例化,將其構造函數設計爲私有;在單例類內部定義了一個Singleton類型的靜態對象,做爲外部共享的惟一實例。

3. 代碼實現

  1. 普通單例模式,多線程調用會出現建立了多個對象
public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

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

}
  1. 餓漢單例模式,是實現起來最簡單的單例類,當類被加載時,靜態變量instance會被初始化,此時類的私有構造函數會被調用,單例類的惟一實例將被建立。可確保單例對象的惟一性。結構圖和代碼以下:

餓漢單例

public class EagerSingleton {

    private static EagerSingleton eagerSingleton = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return eagerSingleton;
    }

}
  1. 懶漢單例模式,在第一次調用getInstance()方法時實例化,在類加載時並不自行實例化,這種技術又稱爲延遲加載(Lazy Load)技術,即須要的時候再加載實例,爲了不多個線程同時調用getInstance()方法,結構圖和代碼以下:

懶漢單例模式

public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {
    }

    //鎖的範圍過大,性能下降
    synchronized public static LazySingleton getInstance() {
        if (null == instance) {
            instance = new LazySingleton();
        }
        return instance;
    }

}

上面的懶漢單例使用synchronzed進行線程鎖,範圍過大,影響性能,可使用雙重檢查鎖定(Double-CheckLocking)來實現懶漢式單例類,代碼以下:安全

class LazySingleton2 {

    //volatile保證多線程間共享變量的可見性
    private volatile static LazySingleton2 instance = null;

    private LazySingleton2() {
    }

    //雙重檢查鎖定(Double-Check Locking)
    public static LazySingleton2 getInstance() {
        //第一重判斷
        if (null == instance) {
            //鎖定代碼塊
            synchronized (LazySingleton2.class) {
                //第二重判斷
                if (null == instance) {
                    instance = new LazySingleton2();
                }
            }
        }
        return instance;
    }
}

餓漢式單例類不能實現延遲加載,無論未來用不用始終佔據內存;懶漢式單例類線程安全控
制煩瑣,並且性能受影響。多線程

還有一種更好的單例實現方式,稱之爲Initialization Demand Holder (IoDH)的技術,既能夠實現延遲加載,又能夠保證線程安全,不影響系統性能.在IoDH中,咱們在單例類中增長一個靜態(static)內部類,在該內部類中建立單例對象,再將該單例對象經過getInstance()方法返回給外部使用,實現代碼以下:函數

class Singleton2 {

    private Singleton2() {
    }

    private static class HolderClass {

        private final static Singleton2 instance = new Singleton2();
    }

    public static Singleton2 getInstance() {
        return HolderClass.instance;
    }

}

4. 優缺點

  • 優勢
  1. 單例模式提供了對惟一實例的受控訪問。
  2. 可節約系統資源,對於需頻繁建立和銷燬的對象單例模式可提升系統的性能。
  3. 容許可變數目的實例,可私用單例控制類似辦法來得到指定個數的對象實例。
  • 缺點
  1. 因爲單例模式中沒有抽象層,所以單例類的擴展有很大的困難。
  2. 單例類的職責太重,在必定程度上違背了「單一職責原則」。
  3. 自動垃圾回收技術可能會致使單例對象狀態的丟失。

5. 適用場景

  1. 系統只須要一個實例對象,如系統要求提供一個惟一的序列號生成器,或者須要考慮資源消耗太大而只容許建立一個對象。
  2. 客戶調用類的單個實例只容許使用一個公共訪問點,除了該公共訪問點,不能經過其餘途徑訪問該實例。

6. 我的理解

單例模式可確保只有一個實例對象,其提供了實例的全局訪問點來得到該實例,爲了不多線程訪問的隱患,可以使用餓漢單例模式和IoDH技術的懶漢單例模式。性能

參考

  1. Java設計模式-劉偉
相關文章
相關標籤/搜索