当前位置: 首页 > news >正文

学习设计模式《四》——单例模式

一、基础概念

        单例模式的本质【控制实例数目】;

        单例模式的定义:是用来保证这个类在运行期间只会被创建一个类实例;单例模式还提供了一个全局唯一访问这个类实例的访问点(即GetInstance方法)单例模式只关心类实例的创建问题,并不关心具体的业务功能

        单例模式的范围:在C#中单例模式的范围是指在每个AppDomain之中只能存在一个实例的类;  在Java中单例的范围是一个虚拟机的范围(因为装载类的功能是虚拟机,一个虚拟机通过自己的ClassLoader装载单例);

        单例模式的命名:建议使用GetInstance()作为单例模式的方法名;GetInstance()方法可以有参数。

何时选用单例模式?

1、当需要控制类的实例只能有一个,且客户只能从一个全局访问点访问它;(常用到单例的场景有:配置内容、数据库等连接资源、文件资源等)。

单例模式的优点
序号单例模式的优点
1  时间与空间
(懒汉式是典型的时间换空间【每次获取实例都会进行判断是否需要创建实例,浪费判断的时间,若一直没有人使用就不会创建实例,节约内存】;
 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用先创建出来,每次调用时就不需要再判断了,节省运行时间
2线程安全
饿汉式是线程安全的,因为在装载的时候只会装载一次,且在装载类的时候不会发生并发;
从线程安全性上来讲,不加同步的懒汉式线程是不安全的【即;有多个线程同时调用GetInstance方法就可能导致并发问题】)

二、单例模式示例

        我们在项目开发过程中,经常会涉及到配置文件的内容;比如我们现在要实现读取配置文件内容,应该如何实现?

 2.1、未使用任何模式

1、编写不使用任何模式直接读取配置文件类

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、客户端使用

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、如下是以读取配置文件类实现为【饿汉式】单例模式的写法:

/***
*	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、 客户端调用饿汉式单例

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、懒汉式单例

        所谓的懒汉式单例,顾名思义:懒,就是不着急,那么在创建对象实例的时候不会立即创建,会一直等到要使用对象实例时才会创建

平时我们使用到的缓存其实也是懒汉式思想(也叫延迟加载)的体现:

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、如下是以读取配置文件类实现为【懒汉式】单例模式的写法:

/***
*	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、 客户端调用饿汉式单例

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 Learnhttps://learn.microsoft.com/zh-cn/dotnet/api/system.appdomain?view=net-6.0托管线程处理基本知识 - .NET | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/standard/threading/managed-threading-basics基于任务的异步编程 - .NET | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/task-based-asynchronous-programming数据并行(任务并行库) - .NET | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/data-parallelism-task-parallel-library1、使用锁控制的【懒汉式】单例模式

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、客户端调用

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 Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/static1、实现不用锁的懒汉式线程安全单例

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、客户端调用

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、编写可控制类实例数量的单例

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、客户端测试

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
}

运行结果如下:

三、项目源码工程

kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern

相关文章:

  • Halcon应用:相机标定
  • Deepseek输出的内容如何直接转化为word文件?
  • 大模型面经 | 介绍一下CLIP和BLIP
  • QSS【QT】
  • React 文章 分页
  • TDengine 性能监控与调优实战指南(一)
  • Docker Compose 外部网络(`external: true`)与内部网络的区别
  • 5、openfeign
  • 对于在线教育或知识付费类网站视频处理方案
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.3.24)
  • 海拔与大气压关系,大气压单位,气压传感器对比
  • DeepSeek与ECharts融合助力复杂图表高效制作
  • 声音识别(声纹识别)和语音识别的区别
  • python爬虫复习
  • SpringBoot 知识图谱
  • 小迪第10天http/s数据包
  • uni-app中map的使用
  • idea中导入从GitHub上克隆下来的springboot项目解决找不到主类的问题
  • FastAPI:现代高性能Python Web框架的技术解析与实践指南
  • PCA 降维实战:从原理到电信客户流失数据应用
  • 上海黄金交易所:贵金属价格波动剧烈,提示投资者做好风险防范
  • 中国房地产报:以改促治实现楼市多难并解
  • 王健林出售酒店管理业务的资金如获批,可用于向全体股东分红
  • 自然资源部一季度新批用海项目中,涉历史遗留围填海项目56个
  • 正义网评“一男两女举办婚礼”:“一夫多妻”流量闹剧该歇了
  • 红十字国际委员会加沙地带办公场所再次遭袭