单例模式
Introduction
单例模式是一种设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点。
场景
这种模式可以用于多种场景,如在手机上的同一个应用程序只能有一个实例的情况下应用。当一个类型的实例只能有一个时,单例模式就派上用场了。如果有多个对象,则可能会出现数据不同步、重复读写等问题。
实现方式
基本实现方式是将构造方法私有化,让实例的过程控制在类的内部去完成并对外部提供一个访问实例的方式。
双重锁定(double-check locking)(懒汉模式)
双重锁定的实现方式是在GetInstance方法中判断实例是否为空,如果为空则进行锁定并再次判断,如果仍为空则创建实例。这种方式是线程安全的,且只有在需要实例的时候才会创建实例。
/// <summary>
/// 双重判空加锁,饱汉模式(懒汉式),用到的时候再去实例化
/// </summary>
public class Singleton
{
private static Singleton _instance;
private static readonly object SyncLock = new object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (_instance == null)
{
lock (SyncLock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
静态初始化 (饿汉模式)
饿汉模式下,在类加载时就创建实例。这种方式在多线程环境下也是线程安全的,但是会在程序启动时就创建实例,可能会导致资源浪费。
/// <summary>
/// 饿汉模式-就是屌丝,担心饿死。类加载就给准备好
/// </summary>
public sealed class Singleton1
{
/// <summary>
/// 静态初始化,由 CLR 去创建
/// </summary>
private static readonly Singleton1 Instance = new Singleton1();
private Singleton1()
{
}
public static Singleton1 GetInstance() => Instance;
}
并发集合(懒汉模式)
这种方式使用ConcurrentDictionary实现单例方法,用到的时候再去实例化。它类似于双重锁定的实现方式,只是使用了并发集合代替了双重判断和锁。
/// <summary>
/// 使用 ConcurrentDictionary 实现的单例方法,用到的时候再去实例化
/// 这种方式类似于第一种方式,只是使用了并发集合代替了双重判断和 lock
/// </summary>
public class Singleton2
{
private static readonly ConcurrentDictionary<int, Singleton2> Instances = new ConcurrentDictionary<int, Singleton2>();
private Singleton2()
{
}
public static Singleton2 GetInstance() => Instances.GetOrAdd(1, k => new Singleton2());
}
Lazy
Lazy是.NET Framework提供的一种线程安全的延迟初始化方式。使用Lazy实现单例模式可以保证线程安全且只在需要实例的时候才会创建实例。
/// <summary>
/// 使用 Lazy 实现的单例方法,用到的时候再去实例化
/// </summary>
public class Singleton3
{
private static readonly Lazy<Singleton3>
LazyInstance = new Lazy<Singleton3>
(() => new Singleton3());
private Singleton3()
{
}
public static Singleton3 GetInstance() => LazyInstance.Value;
}