单例模式

什么是单例模式?

保证一个类只有一个实例,并提供一个全局变量来访问这个实例,这就是单例模式,单例模式属于对象建立型模式。安全

单例模式的几个要素

  • 类只能有一个实例
  • 这个实例必须由该类自己建立
  • 该类必须向整个系统提供该实例的访问权限

单例模式的结构

image.png

单例模式的实现

class Singleton
{
    private static Singleton s_Instance;

    //私有构造方法,防止外部实例化
    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (s_Instance == null)
            s_Instance = new Singleton();

        return s_Instance;
    }
}

测试代码多线程

static void Main()
{
    var s1 = Singleton.GetInstance();
    var s2 = Singleton.GetInstance();
    if (object.ReferenceEquals(s1, s2))
        Console.WriteLine($"s1 s2两个实例是相同的");
    Console.ReadKey();
}

结果
image.png并发

饿汉式单例与懒汉式单例

饿汉式单例(最简单的单例)

image.png

class EagerSingleton
{
    //类加载时执行
    private static EagerSingleton s_Instance = new EagerSingleton();

    private EagerSingleton() { }

    public static EagerSingleton GetInstance()
    {
        return s_Instance;
    }
}

懒汉式单例

//懒汉式单例与饿汉式单例不一样的在于:懒汉式单例在第一次被引用时将本身实例化(类被加载时不会实例化自身)
class LazySingleton
{
    private static LazySingleton s_Instance;

    //私有构造方法,防止外部实例化
    private LazySingleton() { }

    public static LazySingleton GetInstance()
    {
        if (s_Instance == null)
            s_Instance = new LazySingleton();

        return s_Instance;
    }
}

懒汉式单例的线程安全问题

在高并发多线程的环境下运行懒汉式单例的代码,会出如今某一时刻存在多个线程同时访问GetInstance方法,可能会建立多个实例,违背了单例模式的设计意图。高并发

修改懒汉式单例代码使其线程安全性能

class LazySingleton
{
    private static readonly object s_LockObj = new object();

    private static LazySingleton s_Instance;

    //私有构造方法,防止外部实例化
    private LazySingleton() { }

    public static LazySingleton GetInstance()
    {
        if (s_Instance == null)
        {
            //加锁使程序在某一时刻只容许一个线程访问
            lock (s_LockObj)
            {
                if (s_Instance == null)
                    s_Instance = new LazySingleton();
            }
        }

        return s_Instance;
    }
}

为何要双重校验

当实例不存在,而且多个线程同时调用GetInstance方法,此时它们都能经过第一重"s_Instance == null"的判断,此时只会有一个线程进入lock块执行建立代码,若是不进行第二次的"s_Instance == null"判断,当第一个线程建立完实例离开后,第二个线程进入lock块,因为第二个线程并不知道实例已经被建立,将继续建立新的实例,仍是会产生多个实例对象。测试

饿汉式单例与懒汉式单例比较

饿汉式单例在类被加载时就建立实例对象,优势在于无需考虑线程安全问题,因为单例对象一开始就建立好了,因此调用速度优于懒汉式单例。同时,由于类加载时就须要建立单例对象,所以从资源利用角度来讲,饿汉式单例不如懒汉式单例,类的加载时间相对懒汉式单例也更慢。spa

懒汉式单例在第一次使用时建立单例对象,实现了延迟加载,无须一直占用系统资源。可是懒汉式单例必须处理线程安全问题,这将致使系统性能受到必定程度的影响。线程

单例模式的优势

  • 系统中只存在一个对象,节约系统资源
  • 提供惟一一个实例,能够很好的控制如何访问以及何使访问它

单例模式的缺点

  • 没有抽象层,单例类扩展困难
  • 单例类将对象的建立和对象自己的功能耦合在一块儿,职责太重,在必定程度上违背了类的单一职责原则

在如下状况能够考虑使用单例模式

  • 某一个对象在系统中只须要一个或者由于实例化消耗太大只容许建立一个
  • 该实例只能经过一个公共访问点访问
相关文章
相关标签/搜索