设计模式 - 单例模式

单例模式

含义

保证一类仅有一个实例,并提供一个访问它的全局访问点

优缺点

  • 优点:
    • 确保所有对象都访问一个实例
    • 节省内存,避免频繁地创建、销毁对象
    • 避免对共享资源的多重占用
  • 缺点:
    • 违背了"单一职责原则"
    • 可扩展性差

实现过程

  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;
		}
    }
}

参考

posted @ 2019-11-29 16:59  SouthBegonia  阅读(553)  评论(2编辑  收藏  举报