設計模式 - 單例模式


單例模式

含義

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

優缺點

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

實現過程

  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關鍵字實例化單例的先后順序,可把單例模式分為餓漢式單例、懶漢式單例

餓漢式

  • 含義:開始時就實例化instance
  • 優點:線程安全(因為instance是static類型)
  • 缺點:不管是否使用對象,開始時就實例化並占用了空間。即空間換時間
//餓漢式:開始時就實例化instance
public class Singleton 
{  
	private Singleton (){} 
    private static Singleton instance = new Singleton();  
     
    public static Singleton getInstance() 
	{  
    	return instance;  
    }  
}

懶漢式

  • 含義:需要時才實例化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;
		}
    }
}

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM