程式設計模式 - 單例模式 (Singleton)

 是一種常用的軟體設計模式,屬於創建型模式的一種。在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。比如在某個伺服器程序中,該伺服器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在複雜環境下的配置管理。


實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就創建該類的實例並將實例的引用賦予該類保持的引用;同時我們還將該類的構造函數定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。


單例模式在多執行緒的應用場合下必須小心使用。如果當唯一實例尚未創建時,有兩個執行緒同時調用創建方法,那麼它們同時沒有檢測到唯一實例的存在,從而同時各自創建了一個實例,這樣就有兩個實例被構造出來,從而違反了單例模式中實例唯一的原則。 解決這個問題的辦法是為指示類是否已經實例化的變量提供一個互斥鎖(雖然這樣會降低效率)。


有兩個特點:

  • 單一實例 (Single Instance)
  • 延遲建立實例 (Lazy Instantiation)
例子:

非執行緒安全 Singleton
這是個不好的案例,此方式不是執行緒安全,無法確保在多執行緒情況下是唯一的實例。
public sealed class NotThreadSafeSingleton
{
    private static NotThreadSafeSingleton _instance = null;

    private NotThreadSafeSingleton()
    {
    }

    public static NotThreadSafeSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new NotThreadSafeSingleton();
            }

            return _instance;
        }
    }
}

簡單執行緒安全 Singleton
使用 Lock 來確保執行緒安全。
    private static readonly object padlock = new object();

    private static SimpleThreadSafetySingleton _instance = null;

    private SimpleThreadSafetySingleton()
    {
    }

    public static SimpleThreadSafetySingleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (_instance == null)
                {
                    _instance = new SimpleThreadSafetySingleton();
                }
                return _instance;
            }
        }
    }
}

使用 Double-checked Locking 確保執行緒安全
public sealed class DoubleCheckedLockingSingleton
{
    private static readonly object padlock = new object();
    private static DoubleCheckedLockingSingleton _instance = null;

    //用來LOCK建立instance的程序。
    public static DoubleCheckedLockingSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new DoubleCheckedLockingSingleton();
                    }
                }
            }

            return _instance;
        }
    }
}

不使用 Lock, 確保執行緒安全 (非 Lazy)
此範例沒有使用 Lock, 而仍是執行緒安全的 Singleton,但不是 Lazy。
    public sealed class EagerSingleton
    {
        private EagerSingleton()
        {
        }

        public static EagerSingleton Instance { get; } = new EagerSingleton();
    }
}

完整 Lazy 實例
public sealed class LazySingleton
{
    private LazySingleton()
    {
    }

    public static LazySingleton Instance
    {
        get
        {
            return InnerClass.Instance;
        }
    }

    private class InnerClass
    {
        internal static readonly LazySingleton Instance = new LazySingleton();

        static InnerClass()
        {
        }
    }
}

使用 .NET 4 的 Lazy<T>
此方式使用後 .Net Framework 4 之後提供的 Lazy<T>,如此可以簡單的達成 Singleton 要求的 唯一 與 延遲建立。
public sealed class DotNet4LazySingleton
{
    private static readonly Lazy<DotNet4LazySingleton> lazy = new Lazy<DotNet4LazySingleton>(() => new DotNet4LazySingleton());

    private DotNet4LazySingleton()
    {
    }

    public static DotNet4LazySingleton Instance { get { return lazy.Value; } }
}


參考資料:
https://zh.wikipedia.org/zh-tw/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F
https://raychiutw.github.io/2019/%E9%9A%A8%E6%89%8B-Design-Pattern-6-%E5%96%AE%E4%BE%8B%E6%A8%A1%E5%BC%8F-Singleton-Pattern/

留言

熱門文章