单例模式,顾名思义就是一个类只有一个实例的一种实现方式。官方定义:确保一个类只有一个实例,并提供一个全局访问点。如下图:
Singletion |
-uniqueInstance:Singletion |
-Singletion() +GetInstance():Singletion |
解析:
1.Singletion类通过定义一个私有变量uniqueInstance来记录单例类的唯一实例“Singletion”;
2.私有构造函数Singletion()用来防止外界使用new关键字来创建该类的实例;
3.公有方法GetInstance()用来提供该类的唯一全局访问点;
所以,单例模式的一般实现方式为:
1 //单例模式:确保一个类只有一个实例,并提供一个访问它的全局访问点 2 public sealed class Singletion 3 { 4 //定义一个静态变量保存类的实例 5 private static Singletion uniqueInstance; 6 7 //定义私有构造函数,使外界不能创建该类实例 8 private Singletion() { } 9 10 //定义公有方法,提供一个全局访问点;也可以定义公有属性来提供全局访问点 11 public static Singletion GetInstance() 12 { 13 //如果类的实例不存在则创建,否则直接返回 14 if (uniqueInstance == null) 15 { 16 uniqueInstance = new Singletion(); 17 } 18 19 return uniqueInstance; 20 } 21 }
解决单例模式实现思路前,首先思考为什么会有单例模式?它是在什么情况下使用的?
从单例模式的定义中可以看出,单例模式的使用是当我们的系统中某个对象只需要一个实例的情况,例如操作系统中只能有一个任务管理器,操作文件时,同一时间内,只允许有一个实例对其操作,既然现实生活中会有这样的应用场景,那么软件设计必须要有这样的解决方案(因为软件设计也是现实场景的抽象),所以也就有了单例模式。
要实现单例模式,根据定义,首先要确保一个类只有一个实例,如何确保一个类只有一个实例呢?
众所周知,我们创建类的实例的时候,只要通过new调用对象的构造函数即可创建该类的实例(如果我们并未在类中定义构造函数,那么编译器会帮我们自动生成一个公有的无参构造函数),如果构造函数都定义为私有方法,则外界就不能通过new来创建该类的实例。那么怎么创建类的实例呢?
既然构造函数被定义为私有方法,那么则需要对象中提供一个公有方法或属性把该对象的唯一实例公开出去,既然要公开该唯一实例,则需要一个私有的静态变量来存储该实例。为什么该变量要定义为静态的呢?
这是因为静态变量不属于任何实例,而属于类所有。静态变量是在首次引用的时候初始化并一直存储于内存中直至应用程序结束才释放,所以能保证该实例的唯一性。
1.懒汉模式(最常用的模式)
1 //单例模式:确保一个类只有一个实例,并提供一个访问它的全局访问点 2 public sealed class Singletion 3 { 4 //定义一个静态变量保存类的实例 5 private static Singletion uniqueInstance; 6 7 //定义私有构造函数,使外界不能创建该类实例 8 private Singletion() { } 9 10 //定义公有方法,提供一个全局访问点;也可以定义公有属性来提供全局访问点 11 public static Singletion GetInstance() 12 { 13 //如果类的实例不存在则创建,否则直接返回 14 if (uniqueInstance == null) 15 { 16 uniqueInstance = new Singletion(); 17 } 18 return uniqueInstance; 19 } 20 }
该模式可以用于单线程环境,因为在多线程环境下有可能得到Singletion的多个实例。因为两个线程同时访问GetInstance()方法时,此时两个线程判断(uniqueInstance==null)都为真,此时两个线程都会创建Singletion的实例。
2.双重锁模式(解决了线程安全问题)
在介绍双重锁模式之前,先看一个普通的加锁模式示例:
1 //单例模式:确保一个类只有一个实例,并提供一个访问它的全局访问点 2 public sealed class Singletion 3 { 4 //定义一个静态变量保存类的实例 5 private static Singletion uniqueInstance; 6 //定义一个标识确保线程同步 7 private static readonly object locker = new object(); 8 9 //定义私有构造函数,使外界不能创建该类实例 10 private Singletion() { } 11 12 //定义公有方法,提供一个全局访问点;也可以定义公有属性来提供全局访问点 13 public static Singletion GetInstance() 14 { 15 //当第一个线程运行到此,会对locker对象“加锁” 16 //当第二个线程运行到此,首先检测到locker对象为“加锁”状态,该线程就会挂起等待第一个线程解锁 17 //lock语句运行完成之后(即线程运行完之后)会对locker对象“解锁” 18 lock (locker) 19 { 20 //如果类的实例不存在则创建,否则直接返回 21 if (uniqueInstance == null) 22 { 23 uniqueInstance = new Singletion(); 24 } 25 } 26 return uniqueInstance; 27 } 28 }
这种方式确实可以解决多线程的问题,但是上面的代码,每个线程都会对locker对象加锁,之后再判断实例是否存在;如果第一个线程创建了该类的实例,后面的线程直接判断(uniqueInstance==null)就行,而不必对辅助线程locker对象加锁之后再去判断,这样可以节省系统开销,提升性能。因而只需要在lock语句前,增加判断(uniqueInstance==null)就可以避免这种额外的开销,这种实现方式就称为“双重锁定”,具体代码如下:
1 //单例模式:确保一个类只有一个实例,并提供一个访问它的全局访问点 2 public sealed class Singletion 3 { 4 //定义一个静态变量保存类的实例 5 private static Singletion uniqueInstance; 6 //定义一个标识确保线程同步 7 private static readonly object locker = new object(); 8 9 //定义私有构造函数,使外界不能创建该类实例 10 private Singletion() { } 11 12 //定义公有方法,提供一个全局访问点;也可以定义公有属性来提供全局访问点 13 public static Singletion GetInstance() 14 { 15 //当第一个线程运行到此,会对locker对象“加锁” 16 //当第二个线程运行到此,首先检测到locker对象为“加锁”状态,该线程就会挂起等待第一个线程解锁 17 //lock语句运行完成之后(即线程运行完之后)会对locker对象“解锁” 18 //双重锁只需要这一句判断就可以了 19 if (uniqueInstance == null) 20 { 21 lock (locker) 22 { 23 //如果类的实例不存在则创建,否则直接返回 24 if (uniqueInstance == null) 25 { 26 uniqueInstance = new Singletion(); 27 } 28 } 29 } 30 return uniqueInstance; 31 } 32 }
3.饿汉模式
这种方式,首先会在本类内部预先自行实例化出唯一实例,对外提供的唯一访问接口会对预先实例化的唯一实例进行访问。代码如下:
1 //单例模式:确保一个类只有一个实例,并提供一个访问它的全局访问点 2 public sealed class Singletion 3 { 4 //自行预先实例化,内部定义自己唯一的实例,只供内部使用 5 private static Singletion uniqueInstance = new Singletion(); 6 7 //定义私有构造函数,使外界不能创建该类实例 8 private Singletion() { } 9 10 //定义公有方法,提供一个全局访问点;也可以定义公有属性来提供全局访问点 11 public static Singletion GetInstance() 12 { 13 return uniqueInstance; 14 } 15 }
4.泛型单例模式
使用泛型单例模式,要对泛型类进行约束,限制T只能是一个Class,并且有一个公共无参构造函数,代码如下:
1 //定义泛型单例模式模板,T只能是class,并且有一个公共无参构造函数 2 public class SingletionProvider<T> where T : class, new() 3 { 4 private static T uniqueInstance; 5 private static readonly object locker = new object(); 6 7 private SingletionProvider() { } 8 9 public static T GetInstance() 10 { 11 if (uniqueInstance == null) 12 { 13 lock (locker) 14 { 15 if (uniqueInstance == null) 16 { 17 uniqueInstance = new T(); 18 } 19 } 20 } 21 22 return uniqueInstance; 23 } 24 }
调用方式:
1 public class NetIO 2 { 3 public static NetIO GetInstance() 4 { 5 return SingletionProvider<NetIO>.GetInstance(); 6 } 7 }
原文:https://www.cnblogs.com/chenyanbiao/p/9115976.html