设计模式相信很多人在大学都有学过,当时学的时候肯定都学懂了的但在后来的程序开发中却很少会用到,好像其实际意义并不大。下面对其的简单说明希望能对大家的实际开发有所帮助和理解,当然这也仅是我的个人见解。用做自己的备忘录功能。
1、单例模式基本介绍
单例模式是一种确保一个类只有一个实例的设计模式。
适用于需要频繁实例化然后销毁的对象,创建对象消耗资源过多,但又经常用到的对象。或者全局有且仅能有一个实例化的对象。其本质就是保证在整个应用程序的生命周期中 ,任何一个时刻,单例类的实例都只存在一个。
- 特性和功能:确保一个类只有一个实例,并提供一个全局访问点。
- 使用环境:当类只需要一个实例,且易于访问,且实例应在整个应用程序中共享时。
- 注意事项:需要注意线程安全问题。
- 优点:可以确保一个类只有一个实例,减少了内存开销。
- 缺点:没有接口,扩展困难。
2、适用场景(部分)
2.1、 要求生成唯一序列号的环境;
比如记录网站实时登录人数,或者消息报文的编号。
2.2、在整个项目中需要一个共享访问点或共享数据:
例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
2.3、创建一个对象需要消耗的资源过多:
如要访问IO和数据库等资源;
2.4、日志系统:
在应用程序中,通常只需要一个日志系统,以避免在多个地方创建多个日志对象。这一般是由于共享的日志文件一直处于打开状态,所以只能有一个实例去操作,否则内容不好追加也有可能造成资源占用加剧资源消耗。
2.5、数据库连接池(长连接场景):
在应用程序中,数据库连接池是一个非常重要的资源,单例模式可以确保在应用程序中只有一个数据库连接池实例,避免资源浪费。主要是节省打开或者关闭数据库连接所引起的效率损耗,因为何用单例模式来维护,就可以大大降低这种损耗。
2.6、配置文件管理器:
在应用程序中,通常只需要一个配置文件管理器来管理应用程序的配置文件,单例模式可以确保在整个应用程序中只有一个配置文件管理器实例。这个是由于配置文件是共享的资源。
2.7、缓存系统:
在应用程序中,缓存系统是一个重要的组件,单例模式可以确保在整个应用程序中只有一个缓存实例,以提高应用程序的性能。
3、创建方式:
单例模式其结构主要分为三部分:
私有化构造函数:防止外部直接实例化对象。
私有静态成员变量:用于保存唯一的实例。
公有静态方法:提供获取该实例的唯一访问点。
单例模式分为懒汉式和饿汉式
3.1 懒汉式:
类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。
线程安全 (双重检查锁)的懒汉式示例(对于lock不了解的照着用就行):
双重检查的意义:
要实现线程安全其实只用使用lock给静态全局变量加锁即可,这样多个线程同时访问会排队等待。但是当第一个线程进入锁后就已经实例化对象了,后续的线程就算排队进入了也会因为对象已经不为null直接跳出程序,这样看来后面的线程排队进入锁内其实啥事没干就出去了,这就会造成资源的浪费,故此在锁的外面再判断静态变量是否为空这样仅第一个线程需要进入锁内去实例化对象,后续线程可直接返回对象即可。
public class Singleton
{
//定义一个私有的静态全局变量来保存该类的唯一实例
private static Singleton singleton;
//线程锁
private static readonly object _Object = new object();
/// <summary>
/// 私有构造函数
/// </summary>
private Singleton()
{
//必须是私有的构造函数,防止外部通过new来创建该类的实例。
//想要使用该类只能通过唯一访问点GetInstance()。
}
/// <summary>
/// 全局访问点
/// 设置为静态方法则可在外边无需创建该类的实例就可调用该方法,当然你也可以使用属性作为全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
if (singleton == null)//第一重
{
lock (_Object)
{
if (singleton == null)//第二重
{
singleton = new Singleton();
}
}
}
return singleton;
}
}
使用方法:Singleton singletonOne = Singleton.GetInstance();
全局访问点除了使用方法以外还可使用属性进行调用
public class Singleton
{
//静态变量
private static Singleton _instance;
//锁
private static readonly object _lock = new();
//私有构造函数
private Singleton()
{
}
//外部访问属性
public static Singleton Instance
{
get
{
//保证效率,实例化后不再进入lock块
if (_instance == null)
{
//确保线程安全
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
//已经被实例化直接返回
return _instance;
}
}
}
使用方法:Singleton.Instance;
3.2 饿汉式:
类加载就会导致该单实例对象被创建。
在c#中使用静态初始化时无需显示地编写线程安全代码,C# 与 CLR 会自动解决前面懒汉式单例类的多线程同步问题。
public class Singleton
{
// 私有静态成员readonly
private static readonly Singleton instance = new Singleton();
// 私有构造函数,防止外部直接实例化
private Singleton() {}
// 公有静态属性,提供访问单例实例的唯一访问点
public static Singleton Instance
{
get
{
return instance;
}
}
}
到此想必你也看出来懒汉式和饿汉式的区别了吧,说简单点就是懒汉式的实例化是再外部接口里的,饿汉式的实例化是直接实例化的。
3.3 Lazy方式
当然除了上面两种还有一种使用C#的Lazy来实现
public class Singleton
{
//第一个参数是一个工厂方法,返回一个T实例且没有形参;第二个参数true代表需要保证线程安全,false表示不需要线程安全
private static readonly Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton(),true);
public static Singleton Instance { get => _instance.Value; }
private Singleton()
{
}
}
到此就结束了,上面三种方式都是线程安全的可放心使用,至于不安全的写法相比也没人需要吧。。