一、定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种创建型模式。
- 在单例类的内部定义了一个静态对象,作为提供外部共享的唯一实例
- 为了防止在外部对单例类实例化,它的构造函数被设为private
- 在单例类提供一个静态方法或者静态变量(本文均用方法获取实例),让客户可以访问它的唯一实例
二、分类
1、饿汉
由于在C#中调用静态构造函数的时机不是由程序员掌控的,而是当.NET运行时发现第一次使用该类型的时候自动调用该类型的静态构造函数,这样会过早地创建实例,从而降低内存的使用效率。
public class Singleton1
{
private static readonly Singleton1 instance = new Singleton1();
private Singleton1()
{
}
public static Singleton1 GetInstance()
{
return instance;
}
}
2、懒汉
懒汉式也被称为延迟加载,但是多线程下不安全,可能创建多个实例。
public class Singleton2
{
private static Singleton2 instance = null;
private Singleton2()
{
}
public static Singleton2 GetInstance()
{
if (instance == null)
{
instance = new Singleton2();
}
return instance;
}
}
3、双检锁 Double-Checked Locking
在懒汉式的基础上增加双重检验和锁,但是控制繁琐,影响性能。
public class Singleton3
{
private static Singleton3 instance = null;
private static readonly object syncLocker = new object();
private Singleton3()
{
}
public static Singleton3 GetInstance()
{
if (instance == null)
{
lock (syncLocker)
{
if (instance == null)
{
instance = new Singleton3();
}
}
}
return instance;
}
}
很多面试提问会涉及到“锁、多线程”以及线程安全的问题,整点链接以供学习。
https://www.cnblogs.com/MingsonZheng/p/12547288.html
https://www.cnblogs.com/zhao987/p/12551815.html
https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent?view=net-6.0
4、静态内部类
既可以实现延迟加载,又可以保证线程安全,不影响系统性能,但其有一些面向对象的编程语言并不支持此种方式。
public class Singleton4
{
private Singleton4()
{
}
public static Singleton4 GetInstance()
{
return Nested.instance;
}
private class Nested
{
static Nested() { }
internal static readonly Singleton4 instance = new Singleton4();
}
}
5、Lazy
.NET 4或其以上版本,可以使用System.Lazy
public class Singleton5
{
private static readonly Lazy<Singleton5> lazy = new Lazy<Singleton5>(() => new Singleton5());
private Singleton5() { }
public static Singleton5 GetInstance()
{
return lazy.Value;
}
}
6、CAS
Interlocked类,为多个线程共享的变量提供原子操作。
public class Singleton6
{
private static Singleton6 instance = null;
private Singleton6() { }
public static Singleton6 GetInstance()
{
if (instance != null)
{
return instance;
}
var instance_value = new Singleton6();
Interlocked.CompareExchange(ref instance, instance_value, null);
return instance;
}
}
三、测试
单线程都是没有问题的,主要是要注意,多线程下线程安全的问题,举个例子,测试如下:
Console.WriteLine("以5.Lazy为例简单测试");
Singleton5 singleton5_1 = Singleton5.GetInstance();
Singleton5 singleton5_2 = Singleton5.GetInstance();
if (singleton5_1 == singleton5_2)
{
Console.WriteLine("实例唯一\n");
}
Console.WriteLine("以2.懒汉为例多线程不安全测试");
List<Task> singleton2Tasks = new List<Task>();
ConcurrentBag<Singleton2> singleton2List = new ConcurrentBag<Singleton2>();
for (int i = 0; i < 100; i++)
{
singleton2Tasks.Add(Task.Run(() =>
{
singleton2List.Add(Singleton2.GetInstance());
}));
}
Task.WaitAll(singleton2Tasks.ToArray());
int dis2Count = singleton2List.Distinct().Count();
Console.WriteLine($"实例总个数:{singleton2List.Count},去重后个数:{dis2Count},多线程实例{(dis2Count > 1 ? "不" : "")}唯一\n");
Console.WriteLine("以5.懒汉为例多线程安全测试");
List<Task> singleton5Tasks = new List<Task>();
ConcurrentBag<Singleton5> singleton5List = new ConcurrentBag<Singleton5>();
for (int k = 0; k < 100; k++)
{
singleton5Tasks.Add(Task.Run(() =>
{
singleton5List.Add(Singleton5.GetInstance());
}));
}
Task.WaitAll(singleton5Tasks.ToArray());
int dis5Count = singleton5List.Distinct().Count();
Console.WriteLine($"实例总个数:{singleton5List.Count},去重后个数:{singleton5List.Distinct().Count()},多线程实例{(dis5Count > 1 ? "不" : "")}唯一");
Console.ReadLine();
四、前人栽树,后人乘凉
https://cloud.tencent.com/developer/article/1781261
https://www.cnblogs.com/flame7/archive/2020/09/26/13734605.html
https://www.cnblogs.com/edisonchou/p/6618503.html
https://blog.csdn.net/ni996570734/article/details/122958079