浅谈单例模式

今天领导突然问到我,我们之前代码里为什么单例要写if(t == null){ lock(_lock){ if(t == null){ t = new T(); } } }这样的双重if还加锁的写法,于是了解了一下并整理了这边文章。

之前的写法:

public class T
{
    private readonly static object _lock = new object();
    private static T _instance;
    public static T Instance
    {
        get
        {
            if(_instance == null)
            {
                lock(_lock)
                {
                    if(_instance == null)
                    {
                        _instance = new T();
                    }
                }
            }
            return _instance;
        }
    }
}

逻辑:先判断instance是否被创建,若未被创建则加锁,由于多线程的原因,加锁过后还需再判断instance是否被创建,若此时还未被创建则创建实例,操作完成后释放锁。

为什么要双重if:由于多线程访问,有可能一个线程进入锁的时候,实例已经被其他线程所创建,再加一层判断为了防止其他线程创建过后还有创建实例的操作。

 存在问题:

编写繁琐:该写法必须要在每个需要实现单例的对象写这段逻辑;

可读性低:双重 if 的判断和加锁的逻辑不太容易理解,且在不同地方用到是还需注意 if 和 lock 中的逻辑;

效率低:每次获取该实例的时候需要加锁和释放锁,每次加锁和释放锁的操作可能会影响代码运行效率。

优化方案

我主要了解了一种利用微软提供的 Lazy<T>实现懒加载的方法。

public class T
{
    private static readonly Lazy<T> lazyInstance = new Lazy<T>(() => new T());
    public static T Instance => lazyInstance.Value;
    private T()
    {
        // 构造函数是私有的,以防止直接实例化
    }
}

Lazy<T>创建一个延迟初始化的单例实例,lazyInstance是一个静态只读字段,它包含了对Lazy<T>实例的引用。公开Instance属性,提供一个对单例实例的全局访问点,当这个属性第一次被访问时,Lazy<T>会负责初始化单例对象。

优点

简化代码:使用 Lazy<T> 可以避免手动实现双重检查锁定模式,从而简化代码。Lazy<T> 内部已经处理了线程同步的复杂性,开发者只需简单地声明一个 Lazy<T> 实例即可。

线程安全性:Lazy<T> 保证在多线程环境中,即使有多个线程同时尝试访问 Value 属性,也只会创建一个单例实例。Lazy<T> 内部使用高效且安全的同步机制来确保这一点。

性能优化:Lazy<T> 支持延迟初始化,这意味着单例实例只会在第一次访问 Value 属性时被创建。这种方式避免了在程序启动时就创建不必要的对象,从而提高了程序的启动性能。

减少锁竞争:由于 Lazy<T> 的实现细节是封装在 .NET 框架内部的,它可能使用了比手写的同步代码更高效的锁竞争策略。这有助于减少锁竞争,特别是在高并发场景下。

异常安全性:Lazy<T> 在初始化值时能够很好地处理异常。如果在实例化过程中抛出异常,Lazy<T> 会捕获异常并将它存储起来。后续的访问会重新抛出这个异常,而不是创建一个新的实例。

可维护性和可读性:使用 Lazy<T> 实现的单例模式代码更加简洁,易于阅读和维护。它清楚地表达了单例的延迟初始化意图,而不需要深入理解同步机制的细节。

跨平台兼容性:Lazy<T> 是 .NET 标准库的一部分,这意味着使用 Lazy<T> 实现的单例模式可以在不同的 .NET 运行时环境中工作,包括 .NET Framework、.NET Core 和 Xamarin 等。

除此之外还有几种单例的写法:

静态内部类

public class T
{
    private T()
    {
    }
    public static T getInstance()
    {
        return Inner.single;
    }
    private static class Inner
    {
        internal static T single = new T();
    }
}

CAS

public class T
{
    private static T single;
    private T()
    {
    }
    public static T getInstance()
    {
        if (single != null) return single;
        var v = new T();
        Interlocked.CompareExchange(ref single, v, null);
        return single;
    }
}
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值