单例模式-基础说明及Lazy源码解析

定义

单例模式是指在整个应用中,只有一个实例对象的设计模式。

为什么

实际上,单例模式是结合实际的场景进行的设计。比如,一个人只能有一个女朋友。那么我们在设计女友这个类的时候,她只能是单例的。

优点

  1. 一定程度上节约系统资源,提高系统性能(因为不需要对某个类进行频繁的创建)
  2. 避免对共享资源的多重占用(例如文件管理对象)

可以考虑使用单例模式的情况

  1. 需要高频使用的类
  2. 创建对象时消耗资源过多、消耗时间过长,且需要高频使用
  3. 有状态的工具类
  4. 频繁访问数据库或者文件

常见场景

  1. 日志管理、应用配置
  2. 线程池
  3. 数据库连接池
  4. HttpApplication

简单实现

实现关键点

  1. 实例化后的唯一实体
  2. 私有静态构造函数(私有函数定义好后,无法再通过new创建新的实体)
  3. 外部获取唯一实体的途径(提供给外界一个唯一的出口,访问我的唯一实体)

饿汉式

说明这个类的创建过程比较饥渴,所以在类加载的时候就被创建成功了。

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;
            }
        }
    }
}


使用双检锁的懒汉单例有两个关键点:

  1. volatile字段

可以确保变量在访问过程中的可见性,即并发情况下,某线程的操作修改了volatile修改的属性,其他线程获取到的结果是更新后的结果。无这个关键词描述的情况下,获得的结果实际上还是修改前的结果。这属于线程的可见性问题。

  1. 双检锁

第一个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;
    }
}

  1. 私有属性 _state 含义为当前实例化对象的状态,也是锁的对象。
  2. _factory: 待执行函数,按照我们的Lazy使用方式,此处_factory = new T();
  3. _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被清理。

那么为什么说他也使用了双检锁呢?
首先,双检锁的执行循序是:

  1. 判断是否实例化
  2. 对某对象加锁
  3. 再次判断是否进行实例化

而在我们的源码中,结合我的注释可以看到,Lazy执行了类似的逻辑:

  1. 第一次判断state是否为空
  2. 对state加锁
  3. 第二次判断state是否与_state相等
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值