单例模式的定义
对系统中的某些类来说,只有一个实例很重要。例如游戏中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();
}
}