定义

  1. 保证一个类仅有一个实例(私有的静态变量,当前创建了下次不用再次创建),并提供一个它的全局访问点
  2. 要保证是唯一的实例,没有其他实例创建

使用场景

  1. 创建实例太耗费资源,只允许使用一个公共实例,比如 I/O 与数据库的连接等
  2. 全局计数器

代码实现

//懒汉单例类:在第一次被引用时,才将自己实例化
class Singleton
{
   private static Singleton instance; //静态实例,第一次赋值后,将不再为空

    private Singleton(){ } //禁止默认初始化
    
    public static Singleton GetInstance()
    {
         if(instance == null)
         {
               instance = new Singleton();
         }
         return instance;
    }

}

void Main()
{
   //Singleton s1 = new Singleton() //无法实例化,访问级别限制
   Singleton s1 = Singleton.GetInstance();
   Singleton s2 = Singleton.GetInstance();

   if(s1 == s2)
   {
       Console.WriteLine("两个对象是相同的实例");
   }

}


//多线程的实例<涉及到可能会创建多个实例 使用 lock锁>

/*

* lock 可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。
* 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放
  *
* lock 锁的是引用类型的对象,string 类型除外
* 引用类型推荐 静态的、只读的、私有的 对象
* 保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。
  */

class Singleton
{
     private static Singleton instance;
     private static readonly object syncRoot = new object();

     private Singleton(){ }     
    
     //单锁
     public static Singleton GetInstance()
     {
           lock(syncRoot)
           {
                if (instance == null)
                {
                    instance = new SafeSingleton();
                }           
            }       
           return instance;
     }
    
     //DCL double checked locking 双检查锁机制 双重锁定 最佳实践
     public static Singleton GetInstance()
     {
          if(instance == null)   // 如果存在,则return ,否则进入,免去等待时间
          {
               lock(syncRoot)    //必须有,如果没有 则可能造成多实例情况
               {
                     if(instance == null) //必须有,如果在lock锁排队有2个进入,不加判断,第2个也能创建实例
                     {
                            instance = new Singleton();
                     }
               }
          }
          return instance;
      }

}


//静态初始化实例
//饿汉单例类:在自己在被加载时就将自己实例化,会提前占用系统资源,但是解决了多线程不安全问题
public sealed class Singleton //sealed 避免派生,派生可以增加实例
{
     private static readonly Singleton instance = new Singleton();
    

     private Singleton() { }
    
     public static Singleton GetInstance()
     {
          return instance;
     } 

}

优点

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存
  2. 避免对资源的多重占用(比如写文件操作)
  3. 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性
  4. 提供了对唯一实例的受控访问
  5. 允许可变数目的实例
  6. 避免对共享资源的多重占用

缺点

  1. 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
  2. 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态
  3. 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难
  4. 单例类的职责过重,在一定程度上违背了“单一职责原则”
  5. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失