单例模式(饿汉式和懒汉式)
单例模式的定义
对系统中的某些类来说,只有一个实例很重要。例如游戏中Player角色类,玩家角色必须唯一。定义全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。为了解决该问题,提出了让类自身负责保存它唯一实例。这个类保证没有其他实例被创建,并提供一个访问该实例的方法,这就是单例模式的思路。
单例模式(Singleton Pattern)确保某一个类只有一个实例,而自行实例化并向整个系统提供这个实例,这个类被称为单例类,它提供全局访问的方法。它是一种对象创建型模式。
- 单例类只能有一个实例
- 单例类必须自行创建这个实例
- 单例类必须自行向整个系统提供这个实例
模式结构
单例模式实现过程必须:
- 单例类构造函数为私有
- 提供一个自身的静态私有变量
- 提供一个公有的静态工厂方法
结构如下:
单例模式的缺点
- 单例模式没有抽象层,很难对其扩展
- 单例类职责过重,违背了“”单一职责原则“”
饿汉式和懒汉式
1.饿汉式
class Singleton { private static Singleton _instanceSingleton = new Singleton(); private Singleton() { //私有构造 } public static Singleton getInstance() { return _instanceSingleton; } } class Program { static void Main(string[] args) { Singleton XiaoHua = Singleton.getInstance(); } }
2、懒汉式
1 class Singleton 2 { 3 private static Singleton _instanceSingleton; 4 5 private Singleton() 6 {//私有构造 7 } 8 9 public static Singleton getInstance() 10 { 11 if (_instanceSingleton ==null) 12 { 13 _instanceSingleton = new Singleton(); 14 } 15 return _instanceSingleton; 16 } 17 } 18 class Program 19 { 20 static void Main(string[] args) 21 { 22 Singleton XiaoHua = Singleton.getInstance(); 23 } 24 }
比较上面两种写法:
从效率上看:
一个判断一个不判断,判断的浪费时间,但节省空间。不判断的浪费空间,但节省时间。从某种角度优先考虑使用饿汉式。
懒汉式是牺牲时间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。
饿汉式选择牺牲空间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。
从线程安全上看:
不加同步的懒汉式是线程不安全的。假设有两个线程,一个是线程A,一个是线程B,它们同时调用getInstance方法,那就可能导致并发问题,出现两个实例。解决方法可以通过双重检查锁定(Double-CheckLocking)的双重判断机制。
- 当实例不存在且同时有两个线程调用GetInstance()方法时,它们都可以通过第一重“_instanceSingleton == null”判断
- 然后由于lock锁定机制,只有一个线程进入lock中执行创建代码,另一个线程处于排队等待状态
- 必须等待第一个线程执行完毕后才可以进入lock锁定的代码
- 进行第二重“_instanceSingleton == null”判断,是否创建实例
最终改良后的懒汉式:
class Singleton { private static Singleton _instanceSingleton; //程序运行时创建一个静态只读的辅助对象 private static readonly object syncRoot = new object(); private Singleton() {//私有构造 } public static Singleton getInstance() { if (_instanceSingleton == null) { lock (syncRoot)//线程控制 { if(_instanceSingleton == null){ _instanceSingleton = new Singleton(); } } } return _instanceSingleton; } } class Program { static void Main(string[] args) { Singleton XiaoHua = Singleton.getInstance(); } }