一、基础概念
单例模式的本质【控制实例数目】;
单例模式的定义: 是用来保证这个类在运行期间只会被创建一个类实例;单例模式还提供了一个全局唯一访问这个类实例的访问点(即GetInstance方法)单例模式只关心类实例的创建问题,并不关心具体的业务功能。
单例模式的范围: 在C#中单例模式的范围是指在每个AppDomain之中只能存在一个实例的类; 在Java中单例的范围是一个虚拟机的范围(因为装载类的功能是虚拟机,一个虚拟机通过自己的ClassLoader装载单例);
单例模式的命名: 建议使用GetInstance()作为单例模式的方法名;GetInstance()方法可以有参数。
何时选用单例模式?
1、当需要控制类的实例只能有一个,且客户只能从一个全局访问点访问它;(常用到单例的场景有:配置内容、数据库等连接资源、文件资源等)。
|--------|-------------------------------------------------------------------------------------------------------------------------------------------|
| 序号 | 单例模式的优点 |
| 1 | 时间与空间 (懒汉式是典型的时间换空间【每次获取实例都会进行判断是否需要创建实例,浪费判断的时间,若一直没有人使用就不会创建实例,节约内存】; 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用先创建出来,每次调用时就不需要再判断了,节省运行时间) |
| 2 | 线程安全 (饿汉式是线程安全的,因为在装载的时候只会装载一次,且在装载类的时候不会发生并发; 从线程安全性上来讲,不加同步的懒汉式线程是不安全的【即;有多个线程同时调用GetInstance方法就可能导致并发问题】) |
[单例模式的优点]
二、单例模式示例
我们在项目开发过程中,经常会涉及到配置文件的内容;比如我们现在要实现读取配置文件内容,应该如何实现?
2.1、未使用任何模式
1、编写不使用任何模式直接读取配置文件类
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 配置文件类(不使用模式)
/// </summary>
internal class AppConfig
{
private string appConfigPathAndName = $"{Directory.GetCurrentDirectory()}\\AppConfig.txt";
//存放配置文件中参数A的值
private string parameterA;
//存放配置文件中参数B的值
private string parameterB;
public AppConfig()
{
CreateConfig();
ReadConfig();
}
public string AppConfigPathAndName { get => appConfigPathAndName; }
public string ParameterA { get => parameterA; }
public string ParameterB { get => parameterB; }
/// <summary>
/// 读取配置文件,将配置文件中的内容读取出来设置到属性上
/// </summary>
private void ReadConfig()
{
List<string> configList= new List<string>();
using (FileStream fs=new FileStream(appConfigPathAndName,FileMode.Open))
{
using (StreamReader sr=new StreamReader(fs))
{
string strLine=sr.ReadLine();
while (!string.IsNullOrEmpty(strLine))
{
configList.Add(strLine);
strLine = sr.ReadLine();
}
}
if (configList!=null && configList.Count==2)
{
parameterA = configList[0];
parameterB = configList[1];
}
}
}
/// <summary>
/// 创建配置文件
/// </summary>
private void CreateConfig()
{
if (File.Exists(appConfigPathAndName)) return;
using (FileStream fs = new FileStream(appConfigPathAndName, FileMode.Create))
{
using (StreamWriter sw=new StreamWriter(fs))
{
sw.WriteLine("参数A");
sw.WriteLine("参数B");
sw.AutoFlush = true;
}
}
}
}//Class_end
}
2、客户端使用
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ReadAppConfig();
Console.ReadLine();
}
/// <summary>
/// 未使用任何模式读取配置文件
/// </summary>
private static void ReadAppConfig()
{
/*这里是直接使用new来实例化一个操作配置文件的对象AppConfig,会存在什么问题呢?
* 若在系统运行的过程中,有很多地方都需要使用到这个配置内容,
* 那么我们就要在很多地方创建AppConfig对象实例,此时系统就会存在多个AppConfig实例对象,
* 这样会严重浪费内存资源;仔细看一下这些实例对象所包含的内容都是相同的
* (其实只需要一个实例就可以了),我们该如何实现呢?
*/
Console.WriteLine("未使用任何模式读取配置文件");
AppConfig appConfig=new AppConfig();
string str = $"配置文件中 parameterA={appConfig.ParameterA} parameterB={appConfig.ParameterB}";
Console.WriteLine(str);
Console.WriteLine($"未使用任何模式读取配置文件 实例对象{appConfig}的编号={appConfig.GetHashCode()}");
AppConfig appConfig2 = new AppConfig();
string str2 = $"配置文件中 parameterA={appConfig2.ParameterA} parameterB={appConfig2.ParameterB}";
Console.WriteLine(str2);
Console.WriteLine($"未使用任何模式读取配置文件 实例对象{appConfig2}的编号={appConfig2.GetHashCode()}");
}
}//Class_end
}
运行结果如下:

这里是直接使用new来实例化一个操作配置文件的对象AppConfig,会存在什么问题呢?
若在系统运行的过程中,有很多地方都需要使用到这个配置内容, 那么我们就要在很多地方创建AppConfig对象实例,此时系统就会存在多个AppConfig实例对象, 这样会严重浪费内存资源;仔细看一下这些实例对象所包含的内容都是相同的(其实只需要一个实例就可以了),我们该如何实现呢?
2.2、使用单例模式
想要控制一个类只能被创建一个实例;那么首先要做的就是把创建实例的权限收回来,让类自己负责类实例的创建;然后再由这个类提供给外部可以获取该该类实例的方法。既然要收回创建实例的权限,那就需要将类的构造方法私有化。
2.2.1、饿汉式单例
所谓的饿汉式单例顾名思义:就是饿,一饿就比较着急,急需实例,所以一开始就直接创建类的实例。
1、如下是以读取配置文件类实现为【饿汉式】单例模式的写法:
cs
/***
* Title:"设计模式" 项目
* 主题:【饿汉式】单例模式(线程安全)
* Description:
* 基础概念:单例模式的本质【控制实例数目】
* 单例模式:是用来保证这个类在运行期间只会被创建一个类实例;
* 单例模式还提供了一个全局唯一访问这个类实例的访问点(即Instance属性)
* 单例模式只关心类实例的创建问题,并不关心具体的业务功能
*
* 单例模式的范围:在C#中单例模式的范围是指在每个AppDomain之中只能存在一个实例的类
* 在Java中单例的范围是一个虚拟机的范围(因为装载类的功能是虚拟机,一个虚拟机通过自己的ClassLoader装载单例)
*
* 单例模式的命名:建议使用GetInstance()作为单例模式的方法名;GetInstance()方法可以有参数
*
* 单例模式的优点:
* 1、时间与空间
* (懒汉式是典型的时间换空间【每次获取实例都会进行判断是否需要创建实例,浪费判断的时间,若一直没有人使用就不会创建实例,节约内存】
* 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用先创建出来,每次调用时就不需要再判断了,节省运行时间)
* 2、线程安全
* (饿汉式是线程安全的,因为在装载的时候只会装载一次,且在装载类的时候不会发生并发;
* 从线程安全性上来讲,不加同步的懒汉式线程是不安全的【即;有多个线程同时调用GetInstance方法就可能导致并发问题】)
*
* 何时选用单例模式?
* 1、当需要控制类的实例只能有一个,且客户只能从一个全局访问点访问它
*
* Date:2025
* Version:0.1版本
* Author:Coffee
* Modify Recoder:
***/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 【饿汉式】单例模式
/// </summary>
internal class AppConfig_HungrySingleton
{
//1、开始就定义一个变量来存储创建好的类实例
private static AppConfig_HungrySingleton instance=new AppConfig_HungrySingleton();
private string appConfigPathAndName = $"{Directory.GetCurrentDirectory()}\\AppConfig.txt";
//存放配置文件中参数A的值
private string parameterA;
//存放配置文件中参数B的值
private string parameterB;
/// <summary>
/// 2、私有化构造函数
/// </summary>
private AppConfig_HungrySingleton()
{
CreateConfig();
ReadConfig();
}
//3、定义一个方法来为客户端提供AppConfig_HungrySingleton类的实例
public static AppConfig_HungrySingleton GetInstance()
{
return instance;
}
public string AppConfigPathAndName { get => appConfigPathAndName; }
public string ParameterA { get => parameterA; }
public string ParameterB { get => parameterB; }
/// <summary>
/// 读取配置文件,将配置文件中的内容读取出来设置到属性上
/// </summary>
private void ReadConfig()
{
List<string> configList = new List<string>();
using (FileStream fs = new FileStream(appConfigPathAndName, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
string strLine = sr.ReadLine();
while (!string.IsNullOrEmpty(strLine))
{
configList.Add(strLine);
strLine = sr.ReadLine();
}
}
if (configList != null && configList.Count == 2)
{
parameterA = configList[0];
parameterB = configList[1];
}
}
}
/// <summary>
/// 创建配置文件
/// </summary>
private void CreateConfig()
{
if (File.Exists(appConfigPathAndName)) return;
using (FileStream fs = new FileStream(appConfigPathAndName, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("参数A");
sw.WriteLine("参数B");
sw.AutoFlush = true;
}
}
}
}//Class_end
}
2、 客户端调用饿汉式单例
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ReadAppConfig_HungrySingleton();
Console.ReadLine();
}
/// <summary>
/// 【饿汉式】单例模式读取配置文件(线程安全)
/// </summary>
private static void ReadAppConfig_HungrySingleton()
{
Console.WriteLine("\n【饿汉式】单例模式读取配置文件(线程安全)");
AppConfig_HungrySingleton appConfig = AppConfig_HungrySingleton.GetInstance();
string str = $"配置文件中 parameterA={appConfig.ParameterA} parameterB={appConfig.ParameterB}";
Console.WriteLine(str);
Console.WriteLine($"【饿汉式】单例模式读取配置文件(线程安全) 实例对象{appConfig}的编号={appConfig.GetHashCode()}");
AppConfig_HungrySingleton appConfig2 = AppConfig_HungrySingleton.GetInstance();
string str2 = $"配置文件中 parameterA={appConfig2.ParameterA} parameterB={appConfig2.ParameterB}";
Console.WriteLine(str2);
Console.WriteLine($"【饿汉式】单例模式读取配置文件(线程安全) 实例对象{appConfig2}的编号={appConfig2.GetHashCode()}");
for (int i = 0; i <7; i++)
{
Task task = Task.Run(() =>
{
AppConfig_HungrySingleton appConfigTask = AppConfig_HungrySingleton.GetInstance();
Console.WriteLine($"【饿汉式】单例模式读取配置文件(线程安全)_{i} appConfig={appConfigTask.GetHashCode()}");
});
}
}
}//Class_end
}
运行结果如下:

2.2.2、懒汉式单例
所谓的懒汉式单例,顾名思义:懒,就是不着急,那么在创建对象实例的时候不会立即创建,会一直等到要使用对象实例时才会创建。
平时我们使用到的缓存其实也是懒汉式思想(也叫延迟加载)的体现:
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 单例模式的懒汉式还体现了缓存的思想
/// 1、当某些资源或数据被频繁使用,而这些资源或数据存在系统外部(如数据库、硬盘文件等)每次操作
/// 这些数据的时候都得从数据库或磁盘上获取,速度会很慢,造成性能问题
/// 2、一个简单的解决办法就是:把这些数据缓存到内存里面,每次操作的时候,先到内存里面找
/// (看看是否存在这些数据,若有则直接使用;没有就获取它并设置到缓存中,
/// 下次访问就可以直接从内存获取,节省大量时间)缓存是一个种典型的空间换时间的方案
/// </summary>
internal class Cache
{
//定义缓存数据容器
private Dictionary<string,object> _Dic=new Dictionary<string,object>();
/// <summary>
/// 从缓存中获取值
/// </summary>
/// <param name="key">键</param>
/// <returns></returns>
public object GetValue(string key)
{
//先从缓存里面获取值
object obj = _Dic[key];
if (obj==null)
{
//若缓存里面没有,那就去获取对应的数据(如读取数据库或磁盘文件获取)
//我们这里仅作示意,虚拟一个值
obj = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}_{new Random().Next(0,99)}";
//将获取的值设置到缓存里面
_Dic[key] = obj ;
}
//若有值则直接返回
return obj;
}
}//Class_end
}
1、如下是以读取配置文件类实现为【懒汉式】单例模式的写法:
cs
/***
* Title:"设计模式" 项目
* 主题:【懒汉式】单例模式(线程不安全)
* Description:
* 基础概念:单例模式的本质【控制实例数目】
* 单例模式:是用来保证这个类在运行期间只会被创建一个类实例;
* 单例模式还提供了一个全局唯一访问这个类实例的访问点(即Instance属性)
* 单例模式只关心类实例的创建问题,并不关心具体的业务功能
*
* 单例模式的范围:在C#中单例模式的范围是指在每个AppDomain之中只能存在一个实例的类
* 在Java中单例的范围是一个虚拟机的范围(因为装载类的功能是虚拟机,一个虚拟机通过自己的ClassLoader装载单例)
*
* 单例模式的命名:建议使用GetInstance()作为单例模式的方法名;GetInstance()方法可以有参数
*
* 单例模式的优点:
* 1、时间与空间
* (懒汉式是典型的时间换空间【每次获取实例都会进行判断是否需要创建实例,浪费判断的时间,若一直没有人使用就不会创建实例,节约内存】
* 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用先创建出来,每次调用时就不需要再判断了,节省运行时间)
* 2、线程安全
* (饿汉式是线程安全的,因为在装载的时候只会装载一次,且在装载类的时候不会发生并发;
* 从线程安全性上来讲,不加同步的懒汉式线程是不安全的【即;有多个线程同时调用GetInstance方法就可能导致并发问题】)
*
* Date:2025
* Version:0.1版本
* Author:Coffee
* Modify Recoder:
***/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 【懒汉式】单例模式
/// </summary>
internal class AppConfig_IdlerSingleton
{
//1、开始就定义一个变量来存储类实例(不立即创建实例)
private static AppConfig_IdlerSingleton instance = null;
private string appConfigPathAndName = $"{Directory.GetCurrentDirectory()}\\AppConfig.txt";
//存放配置文件中参数A的值
private string parameterA;
//存放配置文件中参数B的值
private string parameterB;
/// <summary>
/// 2、私有化构造函数
/// </summary>
private AppConfig_IdlerSingleton()
{
CreateConfig();
ReadConfig();
}
//3、定义一个方法来为客户端提供AppConfig_IdlerSingleton类的实例
public static AppConfig_IdlerSingleton GetInstance()
{
//4、判断存储实例的变量是否有值
if (instance==null)
{
//5、没有就创建一个类实例,并赋给存储类实例的变量
instance = new AppConfig_IdlerSingleton();
}
//有值就直接返回
return instance;
}
public string AppConfigPathAndName { get => appConfigPathAndName; }
public string ParameterA { get => parameterA; }
public string ParameterB { get => parameterB; }
/// <summary>
/// 读取配置文件,将配置文件中的内容读取出来设置到属性上
/// </summary>
private void ReadConfig()
{
List<string> configList = new List<string>();
using (FileStream fs = new FileStream(appConfigPathAndName, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
string strLine = sr.ReadLine();
while (!string.IsNullOrEmpty(strLine))
{
configList.Add(strLine);
strLine = sr.ReadLine();
}
}
if (configList != null && configList.Count == 2)
{
parameterA = configList[0];
parameterB = configList[1];
}
}
}
/// <summary>
/// 创建配置文件
/// </summary>
private void CreateConfig()
{
if (File.Exists(appConfigPathAndName)) return;
using (FileStream fs = new FileStream(appConfigPathAndName, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("参数A");
sw.WriteLine("参数B");
sw.AutoFlush = true;
}
}
}
}//Class_end
}
2、 客户端调用饿汉式单例
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ReadAppConfig_IdlerSingleton();
Console.ReadLine();
}
/// <summary>
/// 【懒汉式】单例模式读取配置文件(线程不安全)
/// </summary>
private static void ReadAppConfig_IdlerSingleton()
{
Console.WriteLine("\n【懒汉式】单例模式读取配置文件(线程不安全)");
AppConfig_IdlerSingleton appConfig = AppConfig_IdlerSingleton.GetInstance();
string str = $"配置文件中 parameterA={appConfig.ParameterA} parameterB={appConfig.ParameterB}";
Console.WriteLine(str);
Console.WriteLine($"【懒汉式】单例模式读取配置文件(线程不安全) 实例对象{appConfig}的编号={appConfig.GetHashCode()}");
AppConfig_IdlerSingleton appConfig2 = AppConfig_IdlerSingleton.GetInstance();
string str2 = $"配置文件中 parameterA={appConfig2.ParameterA} parameterB={appConfig2.ParameterB}";
Console.WriteLine(str2);
Console.WriteLine($"【懒汉式】单例模式读取配置文件(线程不安全) 实例对象{appConfig2}的编号={appConfig2.GetHashCode()}");
for (int i = 0; i < 7; i++)
{
int tmp = new Random(DateTime.Now.GetHashCode()).Next(1, 4);
Task task =new Task(() =>
{
Thread.Sleep(tmp);
AppConfig_IdlerSingleton appConfigTask = AppConfig_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式读取配置文件(线程不安全)_{i} appConfig={appConfigTask.GetHashCode()}");
});
Thread.Sleep(1);
task.Start();
}
Task task2 = Task.Run(() =>
{
AppConfig_IdlerSingleton appConfigTask2 = AppConfig_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式读取配置文件(线程不安全) appConfig2={appConfigTask2.GetHashCode()}");
});
}
}//Class_end
}
运行结果如下:

注意:懒汉式单例不加同步锁是【线程不安全的】 如:同时有两个线程A和B,它们同时调用GetInstance()方法,就有可能导致并发问题(即:会创建2个实例,导致单例控制在并发情况相爱失效),导致的情况如下图所示:

2.2.3、懒汉式线程安全的单例
**那么该如何实现【懒汉式】单例的线程安全呢?**我们可使用C#的【lock】锁控制;
lock 语句 - 同步对共享资源的访问 - C# reference | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lockAppDomain 类 (System) | Microsoft Learn
https://learn.microsoft.com/zh-cn/dotnet/api/system.appdomain?view=net-6.0托管线程处理基本知识 - .NET | Microsoft Learn
https://learn.microsoft.com/zh-cn/dotnet/standard/threading/managed-threading-basics基于任务的异步编程 - .NET | Microsoft Learn
https://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/task-based-asynchronous-programming数据并行(任务并行库) - .NET | Microsoft Learn
https://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/data-parallelism-task-parallel-library1、使用锁控制的【懒汉式】单例模式
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 线程安全的【饿汉式】单例(使用锁会耗费很多时间在线程同步上)
/// </summary>
internal class ThreadSafe_IdlerSingleton
{
//1、定义一个用于保存实例的静态变量
private static ThreadSafe_IdlerSingleton instance;
//2、定义一个保证线程同步的标识
private static readonly object synchronized=new object();
//3、私有构造函数(外界不能创建该类实例)
private ThreadSafe_IdlerSingleton() { }
//4、创建本类单例实例
public static ThreadSafe_IdlerSingleton GetInstance()
{
//先检查实例是否存在,若不存在在加锁处理
if (instance==null)
{
//同步块,加锁处理
lock (synchronized)
{
//再次判断实例是否存在,不存在才创建
if (instance == null)
{
instance = new ThreadSafe_IdlerSingleton();
}
}
}
return instance;
}
}//Class_end
}
2、客户端调用
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ThreadSafe_IdlerSingletonTest();
Console.ReadLine();
}
/// <summary>
/// 【懒汉式】单例模式(线程安全)
/// </summary>
private static void ThreadSafe_IdlerSingletonTest()
{
Console.WriteLine("\n【懒汉式】单例模式(线程安全)");
Task task = new Task(() =>
{
ThreadSafe_IdlerSingleton threadSafe_IdlerSingleton1 = ThreadSafe_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式(线程安全) threadSafe_IdlerSingleton1的编号是:{threadSafe_IdlerSingleton1.GetHashCode()}");
});
Task task2 = new Task(() =>
{
ThreadSafe_IdlerSingleton threadSafe_IdlerSingleton2 = ThreadSafe_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式(线程安全) threadSafe_IdlerSingleton2的编号是:{threadSafe_IdlerSingleton2.GetHashCode()}");
});
Task task3 = new Task(() =>
{
ThreadSafe_IdlerSingleton threadSafe_IdlerSingleton3 = ThreadSafe_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式(线程安全) threadSafe_IdlerSingleton3的编号是:{threadSafe_IdlerSingleton3.GetHashCode()}");
});
task.Start();
task2.Start();
task3.Start();
}
}//Class_end
}
运行结果如下:

2.2.4、优化版的懒汉式线程安全单例
静态类和静态类成员 - C# | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-membersstatic 修饰符 - C# reference | Microsoft Learn
https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/static1、实现不用锁的懒汉式线程安全单例
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 线程安全的【饿汉式】单例方案二(使用锁会耗费很多时间在线程同步上)
/// </summary>
internal class ThreadSafe2_IdlerSingleton
{
//1、私有化个构造方法
private ThreadSafe2_IdlerSingleton() { }
//2、定义一个没有与该类进行绑定的静态类,只有被调用时才会被装载,从而实现延迟加载
private static class SingletonHolder
{
/*
* 静态初始化【即:只有这个类被装载并被初始化时,会初始化为静态域,从而创建ThreadSafe2_IdlerSingleton的实例】
* 由于是静态域,因此只会在程序装载类时初始化一次,并由AppDomain来保证它的线程安全
*/
internal static readonly ThreadSafe2_IdlerSingleton instance = new ThreadSafe2_IdlerSingleton();
}
//3、创建本类的单例方法
public static ThreadSafe2_IdlerSingleton GetInstance()
{
return SingletonHolder.instance;
}
}//Class_end
}
2、客户端调用
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ThreadSafe2_IdlerSingletonTest();
Console.ReadLine();
}
/// <summary>
/// 【懒汉式】单例模式2(线程安全)
/// </summary>
private static void ThreadSafe2_IdlerSingletonTest()
{
Console.WriteLine("\n【懒汉式】单例模式2(线程安全)");
Task task = new Task(() =>
{
ThreadSafe2_IdlerSingleton threadSafe2_IdlerSingleton1 = ThreadSafe2_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式2(线程安全) threadSafe2_IdlerSingleton1的编号是:{threadSafe2_IdlerSingleton1.GetHashCode()}");
});
Task task2 = new Task(() =>
{
ThreadSafe2_IdlerSingleton threadSafe2_IdlerSingleton2 = ThreadSafe2_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式2(线程安全) threadSafe2_IdlerSingleton2的编号是:{threadSafe2_IdlerSingleton2.GetHashCode()}");
});
Task task3 = new Task(() =>
{
ThreadSafe2_IdlerSingleton threadSafe2_IdlerSingleton3 = ThreadSafe2_IdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】单例模式2(线程安全) threadSafe2_IdlerSingleton3的编号是:{threadSafe2_IdlerSingleton3.GetHashCode()}");
});
task.Start();
task2.Start();
task3.Start();
}
}//Class_end
}
运行结果如下:

2.2.5、可控制实例数量的线程安全单例模式
单例模式是为了控制在运行期间,某些类的实例数目只能有一个;但有时候单个实例并不能满足需要,根据估算,设置为3个实例刚好,那如何实现控制的实例数为3个呢?我们可以借助容器来实现;至于实例的调度算法我们就不深究实现了:
1、编写可控制类实例数量的单例
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SingletonPattern
{
/// <summary>
/// 可控制实例数量的单例模式(线程安全)
/// </summary>
internal class ThreadSafe_MutiIdlerSingleton
{
//私有构造函数
private ThreadSafe_MutiIdlerSingleton() { }
//定义一个保证线程同步的标识
private static readonly object synchronized = new object();
//定义一个缺省的键前缀
private static string defaultPreKey = "sn";
//定义一个缓存实例的容器
private static Dictionary<string, ThreadSafe_MutiIdlerSingleton> dic
= new Dictionary<string, ThreadSafe_MutiIdlerSingleton>();
//定义一个用来记录当前正在使用第几个实例,用以控制最大实例数量,到最大实例数量后,又从1开始
private static int number = 1;
//定一个控制实例的最大数量
private static int maxNum = 3;
public static ThreadSafe_MutiIdlerSingleton GetInstance()
{
string strKey=defaultPreKey+number;
ThreadSafe_MutiIdlerSingleton instance = null;
if (dic.ContainsKey(strKey))
{
instance = dic[strKey];
}
if (instance == null)
{
//同步块,加锁处理
lock (synchronized)
{
//再次判断实例是否存在,不存在才创建
if (instance == null && !dic.ContainsKey(strKey))
{
instance = new ThreadSafe_MutiIdlerSingleton();
dic.TryAdd(strKey, instance);
}
else
{
instance = dic[strKey];
}
}
}
number++;
if (number>maxNum)
{
number = 1;
}
return instance;
}
}//Class_end
}
2、客户端测试
cs
namespace SingletonPattern
{
internal class Program
{
static void Main(string[] args)
{
ThreadSafe_MutiIdlerSingletonTest();
Console.ReadLine();
}
/// <summary>
/// 【懒汉式】可控数量的单例模式(线程安全)
/// </summary>
private static void ThreadSafe_MutiIdlerSingletonTest()
{
Console.WriteLine("\n【懒汉式】可控数量的单例模式(线程安全)");
for (int i = 0; i < 7; i++)
{
Task task = Task.Run(() =>
{
Thread.Sleep(10);
ThreadSafe_MutiIdlerSingleton threadSafe_MutiIdlerSingleton = ThreadSafe_MutiIdlerSingleton.GetInstance();
Console.WriteLine($"【懒汉式】可控数量的单例模式(线程安全) threadSafe_MutiIdlerSingleton_{i}的编号是:{threadSafe_MutiIdlerSingleton.GetHashCode()}");
});
}
}
}//Class_end
}
运行结果如下:
