定义
单例模式是指在整个应用中,只有一个实例对象的设计模式。
为什么
实际上,单例模式是结合实际的场景进行的设计。比如,一个人只能有一个女朋友。那么我们在设计女友这个类的时候,她只能是单例的。
优点
- 一定程度上节约系统资源,提高系统性能(因为不需要对某个类进行频繁的创建)
- 避免对共享资源的多重占用(例如文件管理对象)
可以考虑使用单例模式的情况
- 需要高频使用的类
- 创建对象时消耗资源过多、消耗时间过长,且需要高频使用
- 有状态的工具类
- 频繁访问数据库或者文件
常见场景
- 日志管理、应用配置
- 线程池
- 数据库连接池
- HttpApplication
简单实现
实现关键点
- 实例化后的唯一实体
- 私有静态构造函数(私有函数定义好后,无法再通过new创建新的实体)
- 外部获取唯一实体的途径(提供给外界一个唯一的出口,访问我的唯一实体)
饿汉式
说明这个类的创建过程比较饥渴,所以在类加载的时候就被创建成功了。
using System;
namespace Test.single
{
public class HungrySingle
{
//1. 类加载时被创建,只实例化一次,无线程安全问题
private static readonly HungrySingle _instance = new HungrySingle();
//2. 通过这个私有的静态构造函数,HungrySingle这个类无法通过new进行实例化
private HungrySingle()
{
}
// //3. 方法
// public static HungrySingle getInstance()
// {
// return _instance;
// }
//3. 访问器的方式 两者皆可
public static HungrySingle instance
{
get
{
return _instance;
}
}
}
}
懒汉式
意思是类的创建过程是非饥渴的,所以可以在请求获取实例方法的时候进行初始化。
但是懒汉式往往伴随着问题产生。
最基础的懒汉式代码
using System;
namespace Test.single
{
public class LazySingle
{
private static LazySingle _instance;
private LazySingle()
{
}
public static LazySingle Instance
{
get
{
//此处在多线程情况下存在线程安全问题,instance是临界区
if(_instance == null)
{
_instance = new LazySingle();
}
return _instance;
}
}
}
}
这段代码中的_instance作为多线程的临界资源,是存在线程安全的问题的。为了解决这个问题,可以通过加锁来实现。
双检锁实现
最简单的方式可以是加锁,然后锁内判断是否存在。更优的加锁方式是双检锁。
using System;
namespace Test.single
{
public class DoubleCheckLockSingle
{
//保证每次获取此参数都是直接读取,而不是相信系统缓存
private static volatile DoubleCheckLockSingle _instance;
private static readonly object locker = new object();
private DoubleCheckLockSingle() { }
public static DoubleCheckLockSingle Instance
{
get
{
//判断 提升效率:原因如果某线程提前确认_instance已经存在,无需再次加锁
if (_instance == null)
{
//lock + 判断 ,保证线程安全
lock (locker)
{
if (_instance == null)
{
_instance = new DoubleCheckLockSingle();
}
}
}
return _instance;
}
}
}
}
使用双检锁的懒汉单例有两个关键点:
- volatile字段
可以确保变量在访问过程中的可见性,即并发情况下,某线程的操作修改了volatile修改的属性,其他线程获取到的结果是更新后的结果。无这个关键词描述的情况下,获得的结果实际上还是修改前的结果。这属于线程的可见性问题。
- 双检锁
第一个if,用来提升性能。lock和第二个if才是实现线程安全的保障。
已封装好的Lazy实现
还有一个更简单的实现方式,Lazy
using System;
namespace Test.single
{
public class EasyLazySIngle
{
private static readonly Lazy<EasyLazySIngle> _instance = new Lazy<EasyLazySIngle>(() => new EasyLazySIngle());
private EasyLazySIngle() { }
public EasyLazySIngle Instance
{
get
{
return _instance.Value;
}
}
}
}
Lazy的内部实现也使用了双检锁,没有线程安全问题。
Lazy源码解析
源码如下:
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
public class Lazy<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>
{
//当前实例的状态,使用volatile,保证多线程的可见性
//new LazyHelper(state)
private volatile LazyHelper _state;
// () => new T
private Func<T> _factory;
//实例的值
private T _value;
internal LazyThreadSafetyMode? Mode => LazyHelper.GetMode(_state);
internal bool IsValueFaulted => LazyHelper.GetIsValueFaulted(_state);
public bool IsValueCreated => _state == null;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public T Value
{
get
{
if (_state != null)
{
return CreateValue();
}
return _value;
}
}
//实际使用
//LazyThreadSafetyMode.ExecutionAndPublication 这个锁类型用来保证只有一个线程能够初始化实例
public Lazy(Func<T> valueFactory)
: this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor: false)
{
}
private Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode, bool useDefaultConstructor)
{
if (valueFactory == null && !useDefaultConstructor)
{
throw new ArgumentNullException("valueFactory");
}
_factory = valueFactory;
_state = LazyHelper.Create(mode, useDefaultConstructor);
}
private void ViaFactory(LazyThreadSafetyMode mode)
{
try
{
Func<T> factory = _factory;
if (factory == null)
{
throw new InvalidOperationException(SR.Lazy_Value_RecursiveCallsToValue);
}
_factory = null;
_value = factory();//初始化
_state = null;//这就锁内把_state清理掉了,即使多线程锁在同一个地方,下一个线程获得锁进来后,_state已经与当前获得的executionAndPublication不同了,也不会继续执行了
//保证了线程安全
}
catch (Exception exception)
{
_state = new LazyHelper(mode, exception);
throw;
}
}
private void ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
{
//对_state加锁
lock (executionAndPublication)
{
//第二次判断 判断的是多线程是否导致了变化
if (_state == executionAndPublication)
{
if (useDefaultConstructor)
{
ViaConstructor();
}
else
{
ViaFactory(LazyThreadSafetyMode.ExecutionAndPublication);
}
}
}
}
private T CreateValue()
{
//开始考虑多线程
LazyHelper state = _state;
//第一次为空判断
if (state != null)
{
switch (state.State)
{
case LazyState.NoneViaConstructor:
ViaConstructor();
break;
case LazyState.NoneViaFactory:
ViaFactory(LazyThreadSafetyMode.None);
break;
case LazyState.PublicationOnlyViaConstructor:
PublicationOnlyViaConstructor(state);
break;
case LazyState.PublicationOnlyViaFactory:
PublicationOnlyViaFactory(state);
break;
case LazyState.PublicationOnlyWait:
PublicationOnlyWaitForOtherThreadToPublish();
break;
case LazyState.ExecutionAndPublicationViaConstructor:
ExecutionAndPublication(state, useDefaultConstructor: true);
break;
//命中
case LazyState.ExecutionAndPublicationViaFactory:
ExecutionAndPublication(state, useDefaultConstructor: false);
break;
default:
state.ThrowException();
break;
}
}
return Value;
}
}
- 私有属性 _state 含义为当前实例化对象的状态,也是锁的对象。
- _factory: 待执行函数,按照我们的Lazy使用方式,此处_factory = new T();
- _value: 实例化的实体
我们从Lazy的构造函数开始分析:
第一步,调用构造函数:
//实际使用
//LazyThreadSafetyMode.ExecutionAndPublication 这个锁类型用来保证只有一个线程能够初始化实例
public Lazy(Func<T> valueFactory)
: this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor: false)
{
}
private Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode, bool useDefaultConstructor)
{
if (valueFactory == null && !useDefaultConstructor)
{
throw new ArgumentNullException("valueFactory");
}
_factory = valueFactory;
//通过调用LazyHelper的Create方法,初始化_state状态
_state = LazyHelper.Create(mode, useDefaultConstructor);
}
LazyHelper的源码如下:
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices;
using System.Threading;
internal sealed class LazyHelper
{
internal static readonly LazyHelper NoneViaConstructor = new LazyHelper(LazyState.NoneViaConstructor);
internal static readonly LazyHelper NoneViaFactory = new LazyHelper(LazyState.NoneViaFactory);
internal static readonly LazyHelper PublicationOnlyViaConstructor = new LazyHelper(LazyState.PublicationOnlyViaConstructor);
internal static readonly LazyHelper PublicationOnlyViaFactory = new LazyHelper(LazyState.PublicationOnlyViaFactory);
internal static readonly LazyHelper PublicationOnlyWaitForOtherThreadToPublish = new LazyHelper(LazyState.PublicationOnlyWait);
private readonly ExceptionDispatchInfo _exceptionDispatch;
internal LazyState State { get; }
//LazyState.ExecutionAndPublicationViaFactory 本次使用模式
internal LazyHelper(LazyState state)
{
State = state;
}
internal LazyHelper(LazyThreadSafetyMode mode, Exception exception)
{
switch (mode)
{
case LazyThreadSafetyMode.ExecutionAndPublication:
State = LazyState.ExecutionAndPublicationException;
break;
case LazyThreadSafetyMode.None:
State = LazyState.NoneException;
break;
case LazyThreadSafetyMode.PublicationOnly:
State = LazyState.PublicationOnlyException;
break;
}
_exceptionDispatch = ExceptionDispatchInfo.Capture(exception);
}
internal static LazyHelper Create(LazyThreadSafetyMode mode, bool useDefaultConstructor)
{
switch (mode)
{
case LazyThreadSafetyMode.None:
if (!useDefaultConstructor)
{
return NoneViaFactory;
}
return NoneViaConstructor;
case LazyThreadSafetyMode.PublicationOnly:
if (!useDefaultConstructor)
{
return PublicationOnlyViaFactory;
}
return PublicationOnlyViaConstructor;
//命中 LazyState.ExecutionAndPublicationViaFactory
case LazyThreadSafetyMode.ExecutionAndPublication:
{
LazyState state = (useDefaultConstructor ? LazyState.ExecutionAndPublicationViaConstructor : LazyState.ExecutionAndPublicationViaFactory);
return new LazyHelper(state);
}
default:
throw new ArgumentOutOfRangeException("mode", SR.Lazy_ctor_ModeInvalid);
}
}
}
由此可知,_state中的数据为 LazyHelper的实例。此时基础准备已经构造完成。
我们从中可以看出,实际上需要的T的实例化并没有执行。他的实例化被后移到了调用时。
第二步,调用Value,实例化并返回
private T CreateValue()
{
//开始考虑多线程
LazyHelper state = _state;
if (state != null)
{
switch (state.State)
{
case LazyState.NoneViaConstructor:
ViaConstructor();
break;
case LazyState.NoneViaFactory:
ViaFactory(LazyThreadSafetyMode.None);
break;
case LazyState.PublicationOnlyViaConstructor:
PublicationOnlyViaConstructor(state);
break;
case LazyState.PublicationOnlyViaFactory:
PublicationOnlyViaFactory(state);
break;
case LazyState.PublicationOnlyWait:
PublicationOnlyWaitForOtherThreadToPublish();
break;
case LazyState.ExecutionAndPublicationViaConstructor:
ExecutionAndPublication(state, useDefaultConstructor: true);
break;
//命中
case LazyState.ExecutionAndPublicationViaFactory:
ExecutionAndPublication(state, useDefaultConstructor: false);
break;
default:
state.ThrowException();
break;
}
}
return Value;
}
private void ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
{
//对_state加锁
lock (executionAndPublication)
{
//第二次判断 判断的是多线程是否导致了变化
if (_state == executionAndPublication)
{
if (useDefaultConstructor)
{
ViaConstructor();
}
else
{
ViaFactory(LazyThreadSafetyMode.ExecutionAndPublication);
}
}
}
}
private void ViaFactory(LazyThreadSafetyMode mode)
{
try
{
Func<T> factory = _factory;
if (factory == null)
{
throw new InvalidOperationException(SR.Lazy_Value_RecursiveCallsToValue);
}
_factory = null;
_value = factory();//初始化
_state = null;//这就锁内把_state清理掉了,即使多线程锁在同一个地方,下一个线程获得锁进来后,_state已经与当前获得的executionAndPublication不同了,也不会继续执行了
//保证了线程安全
}
catch (Exception exception)
{
_state = new LazyHelper(mode, exception);
throw;
}
}
这三个方法进行顺序调用,最终的结果就是:
T进行实例化,并保存到_value中。_state和_factory被清理。
那么为什么说他也使用了双检锁呢?
首先,双检锁的执行循序是:
- 判断是否实例化
- 对某对象加锁
- 再次判断是否进行实例化
而在我们的源码中,结合我的注释可以看到,Lazy执行了类似的逻辑:
- 第一次判断state是否为空
- 对state加锁
- 第二次判断state是否与_state相等