一个类只容许建立惟一一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫做单例设计模式,简称单例模式。git
单例模式多是你们据说最多的设计模式了,网上介绍最多的设计模式大概就是单例模式了,我看过的设计模式相关的文章不少都是写一篇介绍单例模式,而后就没有了。程序员
经典的设计模式有 23 种, 若是随便抓一个程序员,让他说一说最熟悉的 3 种设计模式,那其中确定会包含今天要讲的单例模式,github
单例模式主要用来确保某个类型的实例只能有一个。好比手机上的蓝牙之类的只能有一个的实例的场景能够考虑用单例模式。设计模式
主要做用:安全
单例模式的实现,一般须要私有化构造方法,防止外部类直接使用单例类的构造方法建立对象多线程
public class Singleton { private static Singleton _instance; private Singleton() { } public static Singleton GetInstance() { if (_instance == null) { _instance = new Singleton(); } return _instance; } }
这种方式比较简单,可是不是线程安全的,多线程高并发状况下可能会致使建立多个实例,可是若是你的业务场景容许建立多个,我以为问题也不大,若是必定要保证只能建立一个实例,能够参考下面的作法架构
/// <summary> /// 双重判空加锁,饱汉模式(懒汉式),用到的时候再去实例化 /// </summary> public class Singleton { private static volatile 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; }
这也是一种常见的实现单例模式的用法,可是这种方式就不支持懒加载了,不像上面那种方式能够作到须要的时候再实例化,适用于这个对象会被频繁使用或者这个类比较小,是否实例化没有什么影响。框架
这个是以前忘记在哪里看到的微软框架里的一段代码,相似,可能和源码并不彻底同样,只是提供一种实现思路asp.net
/// <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()); }
C# 里提供了 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; }
你也可使用内部类等实现方式,这里就不介绍了,想了解能够本身网上找一下
验证是否线程安全,验证示例代码:
Console.WriteLine($"Singleton"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton1"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton1.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton2"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton2.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton3"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton3.GetInstance().GetHashCode()}"); })).WhenAll().Wait();
上面的 WhenAll
是一个扩展方法,就是调用的 Task.WhenAll
,输出示例:
随着如今依赖注入思想的普及,asp.net core 更是基于依赖框架构建的,使用依赖注入的方式能够较好的解决上面的各类问题
基于依赖注入框架,你能够没必要担忧对象的建立和销毁,让依赖注入框架管理对象,这样这个要实现单例模式的类型能够和其余普通类型同样,只须要使用依赖注入框架注册服务的时候指定服务生命周期为单例便可,好比使用微软的依赖注入框架的时候可使用 services.AddSingleton<TSingletonService>();
来注册单例服务