前言
单例模式是常用的设计模式之一,单例模式只允许被其自身实例化一次,且向外部提供了一个访问该实例的属性。通常来说,单例对象进行实例化时一般不带参数,因为如果不同的实例化请求传递的参数不同的话会导致问题的产生。(若多个请求都是传递的同样的参数的话,工厂模式更应该被考虑)
C#中实现单例有很多种方法,本文将按顺序介绍懒汉式、饿汉式、Lazy< T > 三种方法
单例模式:保证进程中,类型只有一个实例
- 构造函数私有化,防止他人实例化
- 提供一个公开、静态的获取实例的方法
- 返回共用一个静态字段
- 类前面加sealed 密封类
加 sealed 关键字
防止继承。在单例模式中,我们希望确保这个类只有一个实例,而且不能被继承
提高性能:sealed 类可以被编译器进行某些优化,因为它知道这个类不会被继承。
明确设计意图:告诉其他开发者,这个类不应该被继承。
单例模式写法
一、懒汉式
什么是懒汉式?懒 代表 只有在第一次使用的时候才会加载,如果不使用那么就不会去加载了
- 双IF语句判断加锁
//懒汉式 sealed关键字
public sealed class Singleton_lazy
{
//创建私有静态实例
private static Singleton_lazy instance;
//私有锁
private static readonly object _lock = new object();
private Singleton_lazy() { }
//创建静态的、公开的构造实例方法
public static Singleton_lazy Getinstance()
{
if(instance == null)
{
lock (_lock)
{
if(instance == null)
{
instance = new Singleton_lazy();
}
}
}
return instance;
}
static void Main()
{
var instance1 = Singleton_lazy.Getinstance();
var instance2 = Singleton_lazy.Getinstance();
//比较两个对象是否相同
Console.WriteLine(ReferenceEquals(instance1, instance2)); //输出 True
Console.ReadKey();
}
`}
`
特点:
-
只有在首次访问时才创建实例
-
使用双重检查锁定(double-check locking)来确保线程安全
-
volatile
关键字的作用 -
内存可见性 :多运用在多线程环境中,当一个字段被声明为
volatile
,它确保所有线程在访问该字段时都会直接从主内存读取最新的值,而不是从本地缓存中读取。这意味着一个线程对volatile
字段的修改对其他线程是立即可见的。 -
readonly
关键字的作用 -
确保字段不可变。保证字段的值在对象创建后不可修改:一旦字段被赋值(在构造函数中),它的值就不能被改变。这对于确保数据一致性和保护对象的状态非常有用。
-
创建不可变类型:使用 readonly 字段可以帮助实现不可变对象。这些对象在创建后无法更改,因此是线程安全的,适合在多线程环境中使用。
二、饿汉式
- (推荐)静态字段:由CLR保证,在第一次使用这个类型之前,自动初始化并且只初始化一次
//饿汉式 使用静态字段
public sealed class Singleton_hungry
{
private Singleton_hungry() { }
private static readonly Singleton_hungry _singleton_hungry = new Singleton_hungry();
public static Singleton_hungry GetInstance
{
get { return _singleton_hungry; }
}
static void Main()
{
var instance1 = Singleton_hungry.GetInstance;
var instance2 = Singleton_hungry.GetInstance;
//比较两个对象是否相同
Console.WriteLine(ReferenceEquals(instance1, instance2)); //输出 True
Console.ReadKey();
}
`}
`
- 静态构造函数:由CLR保证,在第一次使用这个类型之前,自动被调用且只调用一次
static SingletonPatternSecond()
{
_singletonPatternSecond = new SingletonPatternSecond();
}
三、Lazy< T > (推荐使用)
此内容查看价格为2.9lincol代币(包年VIP免费),请先登录
此隐藏内容仅限包年VIP免费查看。包年VIP仅10元,建议升级。VIP可享有哪些特权?
三种方式总结
- 如果单例的创建和初始化非常耗资源,并且不是每次都需要使用,那么懒汉式更合适。
- 如果单例的创建和初始化不耗费太多资源,并且每次运行时都会用到,那么饿汉式可能更简单直接。
- 在现代 .NET 开发中,使用 Lazy 的方式通常是最推荐的,因为它既保证了懒加载,又保证了线程安全,而且实现简单。
单例有什么用?用在哪个场景
同一个实例,不能解决多线程并发问题!会有线程冲突,覆盖数据
单例就是程序只需要这个对象实例化一次
实际应用场景:
- 唯一序列号
- 数据库连接池:数据库连接---非托管资源---申请/释放消耗性能。池化资源---内置10个连接---使用来拿,用完放回去避免重复申请和销毁---控制连接数量
- 线程池
- 流水号生成器
- 配置文件读取
- IOC容器实例
文章参考资料
- C#文章集合:更全面的C#知识点文章
- 【设计模式】C#实现结构型模式之-适配器模式:结构型模式------适配器模式
- 【设计模式】C#实现行为型模式之-策略模式:行为型模式------策略模式
- 【设计模式】C#实现创建型模式之-简单工厂、工厂方法、抽象工厂模式:创建型模式------工厂模式