設計模式 - 單例模式

單例模式

含義: 保證一類僅有一個實例,並提供一個訪問它的全局訪問點

優缺點:

  • 優勢:
    • 確保全部對象都訪問一個實例
    • 節省內存,避免頻繁地建立、銷燬對象
    • 避免對共享資源的多重佔用
  • 缺點:
    • 違背了"單一職責原則"
    • 可擴展性差

實現過程:

  1. 保證單一實例:建立private static類型的對象instance,其爲空時才new實例化建立
  2. 全局訪問點:建立public類GetInstance()方法用於全局訪問instance,並擔當檢測、new instance的責任

概念圖:

代碼實現:

//單例類:
class Singleton
{
    //私有化的構造函數
    private Singleton() { Console.WriteLine("This is a SingleObject"); }

    //單一的實例  
    private static Singleton instance;         
 
    //全局訪問點
    public  static Singleton GetInstance()
    {
        //若是實例爲空,則new一個新實例
        if (instance == null)
        {
            instance = new Singleton();
            Console.WriteLine("Create a new Instance");
        }
        else
        {
            Console.WriteLine("Get a Instance");                           
        }
        return instance;
    }
}

//測試類:
class Program
{
    static void Main(string[] args)
    {
        Singleton s1 =  Singleton.GetInstance2();
        Singleton s2 =  Singleton.GetInstance2();

        /* OUT:
         This is a SingleObject
         Create a new Instance
         Get a Instance
        */
    }
}

單例模式分類

根據new關鍵字實例化單例的前後順序,可把單例模式分爲餓漢式單例、懶漢式單例html

餓漢式:開始時就實例化instance設計模式

  • 優勢:線程安全(由於instance是static類型)
  • 缺點:無論是否使用對象,開始時就實例化並佔用了空間。即空間換時間

懶漢式:須要時才實例化instance安全

  • 優勢:資源利用率高。即時間換空間
  • 缺點:多線程下存在隱患
//餓漢式:開始時就實例化instance
public class Singleton 
{  
    private Singleton (){} 
    private static Singleton instance = new Singleton();  
     
    public static Singleton getInstance() 
    {  
        return instance;  
    }  
}

//懶漢式:須要時才實例化instance
//上一段代碼同爲懶漢式
public class Singleton 
{   
    private Singleton (){} 
    private static Singleton instance;  
     
    public static Singleton getInstance() 
    {  
        if (instance == null) 
            instance = new Singleton();  
        return instance;  
    }  
}

多線程中的單例

在上述懶漢式單例中,若多個線程同時進行到if (instance == null),則所有檢測經過,也就會形成多線程下建立了多個實例,即 多線程不安全。所以須要對多線程下的單例模式進行調整,實現線程安全,方法是 lock機制多線程

//餓漢式 + 線程安全 + 單鎖
class Singleton
{
    private Singleton() {}
    private static Singleton instance;       
    
    //靜態只讀對象用於輔助實現lock
    private static readonly object locker = new object();

    public static Singleton GetInstance()
    {
        //lock機制:確保一個線程位於該臨界區時,其餘線程不可再進入,直至當前線程訪問完畢
        lock (locker)
        {
            if (instance == null)
                instance = new Singleton();
        }
        return instance;
    }
}

雖然加了lock鎖實現了懶漢模式下的線程安全,但咱們不難發現一個問題:若已經存在instance實例,在執行Getinstance()時還有必要lock{}嗎?顯然不須要,lock的使用必然是消耗必定空間的,所以爲了節省lock的空間,採用更優解法:雙重鎖定(Double-Check-Locking)函數

//餓漢式 + 線程安全 + 雙鎖
class Singleton
{
    private Singleton() {}
    private static Singleton instance;       
    
    //靜態只讀對象用於輔助實現lock
    private static readonly object locker = new object();

    public static Singleton GetInstance()
    {
        //首次檢驗:是否須要加鎖
        if(instance == null)
        {
            //爲當前線程加鎖
            lock (locker)
            {
                //再次檢驗:避免當前線程實例化instance後,下一個線程再次實例
                if (instance == null)
                    instance = new Singleton();
            }
            return instance;
        }
    }
}

參考

相關文章
相關標籤/搜索