保证一个类只有一个实例,并提供一个全局变量来访问这个实例,这就是单例模式,单例模式属于对象建立型模式。安全
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(); }
结果并发
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
懒汉式单例在第一次使用时建立单例对象,实现了延迟加载,无须一直占用系统资源。可是懒汉式单例必须处理线程安全问题,这将致使系统性能受到必定程度的影响。线程