c#设计模式(1)——单例模式
一、引言
最近看了一些单例模式的介绍,感觉可以总结一下,形成自己的笔记。部分内容选自https://www.cnblogs.com/zhili/p/SingletonPatterm.html,感觉写得很好,我只是再加点东西
二、介绍
单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
三、多种单例模式的创建方式
1.饿汉式创建单例模式
说明:饿汉就是类一旦加载,就把单例初始化完成,保证CreateSingleton的时候,单例是已经存在的了。饿汉式创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
/// <summary>
/// 饿汉式写法
/// </summary>
public class SingletonEH
{
/**
*是否 Lazy 初始化:否
*是否多线程安全:是
*实现难度:易
*描述:这种方式比较常用,但容易产生垃圾对象。
*优点:没有加锁,执行效率会提高。
*缺点:类加载时就初始化,浪费内存。
*它基于 classloder 机制避免了多线程的同步问题,
* 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
* 在单例模式中大多数都是调用 CreateSingleton 方法,
* 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
* 这时候初始化 instance 显然没有达到 lazy loading 的效果。
*/
private static SingletonEH instance = new SingletonEH();
private SingletonEH()
{
Console.WriteLine($"{typeof(SingletonEH).Name}被构造。。。");
}
public static SingletonEH CreateSingleton()
{
return instance;
}
}
2.懒汉式创建单例模式
说明:而懒汉比较懒,只有当调用CreateSingleton的时候,才回去初始化这个单例。懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
/// <summary>
/// 懒汉式
/// </summary>
public class SingletonLH
{
/**
*是否 Lazy 初始化:否
*是否多线程安全:是
*实现难度:易
*描述:这种方式比较常用,但容易产生垃圾对象。
*优点:没有加锁,执行效率会提高。
*/
private static SingletonLH instance = null;
private SingletonLH()
{
Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
}
public static SingletonLH CreateInstance()
{
if(instance == null)
{
return new SingletonLH();
}
return instance;
}
}
3.上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行CreateInstance方法时,此时两个线程判断(instance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使CreateInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了。
public class SingletonLH
{
/**
*是否 Lazy 初始化:否
*是否多线程安全:是
*实现难度:易
*描述:这种方式比较常用,但容易产生垃圾对象。
*优点:没有加锁,执行效率会提高。
*缺点:类加载时就初始化,浪费内存。
*它基于 classloder 机制避免了多线程的同步问题,
* 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
* 在单例模式中大多数都是调用 CreateInstance 方法,
* 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
* 这时候初始化 instance 显然没有达到 lazy loading 的效果。
*/
private static SingletonLH instance = null;
private static readonly object locker = new object();
private SingletonLH()
{
Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
}
public static SingletonLH CreateInstance()
{
lock (locker)
{
if(instance == null)
{
return new SingletonLH();
}
}
return instance;
}
}
上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(instance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(instance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,
public class SingletonLH
{
private static SingletonLH instance = null;
private static readonly object locker = new object();
private SingletonLH()
{
Console.WriteLine($"{typeof(SingletonLH).Name}被构造。。。");
}
public static SingletonLH CreateInstance()
{
if(instance == null)
{
lock (locker)
{
if(instance == null)
{
return new SingletonLH();
}
}
}
return instance;
}
}
4. 泛型封装单例模式,利于重复利用
a.新建一个类 BaseSingleton类
public class BaseSingleton<T> where T: new()
{
protected static T _instance;
private static readonly object locker = new object();
public static T getInstance()
{
if(_instance == null)
{
lock (locker)
{
if(_instance == null)
{
_instance = new T();
}
}
}
return _instance;
}
}
b.需要用到的类继承调用
public class Singleton: BaseSingleton<Singleton>
{
public int Id { get; set; }
public string Name { get; set; }
public static void Test()
{
Console.WriteLine("this is Test...");
}
}

浙公网安备 33010602011771号