定义
百度文库:单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
标准定义:Singleton保证一个类仅有一个实例,并提供一个访问它的全局访问点。
UML图
示例
(1.1)定义一个私有的静态的全局变量
public class Singleton { //定义一个私有的静态的全局变量 private static Singleton singleton; }
(1.2)设置私有构造函数
public class Singleton { //定义一个私有的静态的全局变量 private static Singleton singleton; /// <summary> /// 设置私有构造函数(外部无法使用new创建实例) /// </summary> private Singleton() { } }
(1.3)设置一个全局访问点,静态方法供外部调用
public class Singleton { //定义一个私有的静态的全局变量 private static Singleton singleton; /// <summary> /// 设置私有构造函数(外部无法使用new创建实例) /// </summary> private Singleton() { } /// <summary> /// 设置一个全局访问点,静态方法供外部调用 /// </summary> /// <returns></returns> public static Singleton GetInstance() { //判断保证实例化一次 if (singleton == null) singleton = new Singleton(); return singleton; } }
(1.4)调用代码
class Program { static void Main(string[] args) { Singleton instanceA = Singleton.GetInstance(); Singleton instanceB = Singleton.GetInstance(); Console.WriteLine(instanceA.Equals(instanceB) ? "一个实例" : "不是一个实例"); Console.ReadKey(); } }
结果显示一个实例,那么一个初步的单例模式我们就体验完毕了
优化与思考
刚刚我们属于单线程,那有没有这样一种情况,有多个线程一起创建当前实例,一个线程刚刚进入判断IF中,另一个也进来了,此时兴许有实例化2次的情况,这样在某种情况下虽然不会报错,但是默认多次实例化却偏离了我们单例的初衷,下面我们来大概模拟一下这种情况.
(2.1)修改创建-我们在实例化代码位置随意输出内容判断进入次数
public class Singleton { //定义一个私有的静态的全局变量 private static Singleton singleton; /// <summary> /// 设置私有构造函数(外部无法使用new创建实例) /// </summary> private Singleton() { } /// <summary> /// 设置一个全局访问点,静态方法供外部调用 /// </summary> /// <returns></returns> public static Singleton GetInstance() { //判断保证实例化一次 if (singleton == null) { singleton = new Singleton(); Console.WriteLine(DateTime.Now.ToString()); } return singleton; } }
这里我输出了时间
(2.2)调用多线程这里我们用2个线程举例
class Program { static void Main(string[] args) { Singleton instance = null; Parallel.For(0, 2, (i) => { instance = Singleton.GetInstance(); }); Console.ReadKey(); } }
(2.3)结果居然出现了2次,也就是实例化2次
分析
为了避免这种情况,我们可以将线程锁定。意味着锁定之后每次只会有一个线程进入判断,实例化对象。一定程度避免了多次实例化。
(3.1)增加只读静态对象("锁"对象)
//只读静态对象 private static readonly object syncObject = new object();
(3.2)实例化加入判断
/// <summary> /// 设置一个全局访问点,静态方法供外部调用 /// </summary> /// <returns></returns> public static Singleton GetInstance() { if (singleton == null) { lock (syncObject) { //判断保证实例化一次 singleton = new Singleton(); Console.WriteLine(DateTime.Now.ToString()); } } return singleton; }
(3.3)结果
???怎么回事,怎么又出现2次了。。#¥¥&&@@@)¥)(@*@
所以最终我们双重锁定来解决问题在lock内部再加一层判定
/// <summary> /// 设置一个全局访问点,静态方法供外部调用 /// </summary> /// <returns></returns> public static Singleton GetInstance() { if (singleton == null) { lock (syncObject) { //判断保证实例化一次 if (singleton == null) { singleton = new Singleton(); Console.WriteLine(DateTime.Now.ToString()); } } } return singleton; }
结果:
补充:
我们上边的例子采用的都是lazy load也就是懒加载,用的时候加载,当然还有一种方式就是饿汉模式,就是类初始化的时候直接实例化实例
/// <summary> /// 饿汉 /// </summary> public sealed class SingletonHungry { private static readonly SingletonHungry singleton = new SingletonHungry(); public static SingletonHungry GetInstance() { return singleton; } }
优点
缺点
来源:https://www.cnblogs.com/mongo/p/4546048.html