C#设计模式之【创建型设计模式:单例模式】
一.单例模式简述
单例模式: 保证进程中,某个类只有一个实例,并提供一个访问它的全局访问点。
优点: 单例可以避免重复创建,减少资源消耗,主要应对一些特殊情况,比如数据库连接池(内置了资源), 全局唯一号码生成器等。
缺点: 但是使用单例模式也会常驻内存,而且它的变量不是线程安全的,除非是真的有必要,否则不要单例。来自老年人的忠告。。。
UML图
二.几种实现方式
1. 非线程安全
先写一个类
/// <summary>
/// 非线程安全写法
/// </summary>
public class Singleton1
{
private static Singleton1 _Singleton1 = null;
private static int total=0;//私有变量,用于探究线程安全
/// <summary>
/// 构造函数
/// </summary>
private Singleton1()
{
Thread.Sleep(1000);//先休眠1秒,为了探究线程安全
Console.WriteLine($"{this.GetType().Name}被创建");
}
/// <summary>
/// 创建实例的方法
/// </summary>
/// <returns></returns>
public static Singleton1 CreatesSingleton1()
{
if (_Singleton1 == null)
{
_Singleton1 = new Singleton1();
}
return _Singleton1;
}
/// <summary>
/// 对total做++运算
/// </summary>
public void AddTotal()
{
total++;
}
/// <summary>
/// 输出total的值
/// </summary>
public static void ShowTotal()
{
Console.WriteLine($"输出total的值为{total}");
}
}
然后在控制台开辟10000个线程,同时调用CreatesSingleton1获取对象实例。看看结果如何
事实证明–>这个写法不行
2. 线程安全
为了解决线程安全问题,我们需要加锁,代码修改如下:
/// <summary>
/// 线程安全写法
/// </summary>
public class Singleton2
{
//volatile 促进线程安全 让线程按顺序操作
private static volatile Singleton2 _Singleton2 = null;
//用于加锁
private static readonly object Singleton_Lock = new object();
//私有变量,用于探究线程安全
private static int total = 0;
/// <summary>
/// 构造函数
/// </summary>
private Singleton2()
{
Thread.Sleep(1000);//先休眠1秒,为了探究线程安全
Console.WriteLine($"{this.GetType().Name}被创建");
}
/// <summary>
/// 创建实例的方法
/// </summary>
/// <returns></returns>
public static Singleton2 CreatesSingleton2()
{
if (_Singleton2 == null)//第一次判断,如果已有值,不需要加锁
{
lock (Singleton_Lock)//加锁
{
if (_Singleton2 == null)//第二次判断,保证只创建一次(很关键)
{
_Singleton2 = new Singleton2();
}
}
}
return _Singleton2;
}
/// <summary>
/// 对total做++运算
/// </summary>
public void AddTotal()
{
lock (Singleton_Lock)//加锁
{
total++;
}
}
/// <summary>
/// 输出total的值
/// </summary>
public static void ShowTotal()
{
Console.WriteLine($"输出total的值为{total}");
}
}
3. 静态构造函数(饿汉式)
为了保证单例频繁的加锁,会导致性能损耗,我们可以利用静态构造函数的特点,来实现单例模式(上面已经讨论变量的线程安全问题了,这里就不再探究。)
/// <summary>
/// 使用静态构造函数:由CLR保证,程序第一次使用这个类型前被调用,且只调用一次
/// </summary>
public class Singleton3
{
private static Singleton3 _Singleton3 = null;
static Singleton3()
{
_Singleton3 = new Singleton3();
Console.WriteLine($"Singleton3被构造");
}
public static Singleton3 CreatesSingleton3()
{
return _Singleton3;
}
}
4. 静态字段(饿汉式)
我们还可以使用静态字段来实现单例模式
/// <summary>
/// 静态字段:在第一次使用这个类之前,由CLR保证,初始化且只初始化一次
/// </summary>
public class Singleton4
{
private static Singleton4 _Singleton4 = new Singleton4();
private Singleton4()
{
Console.WriteLine($"{this.GetType().Name}被构造");
}
public static Singleton4 CreatesSingleton4()
{
return _Singleton4;
}
}