单例模式

介绍

什么是单例模式呢?
我们可以将其理解为,只能有一个实例的类。比较官方的一种说法是:确保一个类只有一个实例,并提供一个全局访问点。

实现思路

确保这个类只能有一个实例

如何确保这个类只能有一个实例呢?
如果我们要在外部类使用该类的一些属性和方法,那么我们就需要在外部类使用new关键字创建一个该类的实例。我们怎样让该类不能在外部类使用new创建实例呢?我们可以在该类内部定义一个私有的构造函数,那么这个类不能在外界通过new来创建实例了。(注:如果类中没有定义构造函数,那么编译器会帮我们生成一个公有的无参构造函数)

1
2
3
4
5
6
7
8
9
10
public class Singleton
{
//创建一个静态变量来保存类的实例
private static Singleton _instance;

//定义私有构造函数,使外界不能使用new创建该类实例
private Singleton()
{
}
}

提供一个全局访问点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Singleton
{
//创建一个静态变量来保存类的实例
private static Singleton _instance;

//定义私有构造函数,使外界不能使用new创建该类实例
private Singleton()
{
}

//定义一个公有的方法或属性为外部提供一个访问该类的全局访问点
//此处以属性为例
public static Singleton Instance
{
get
{
//如果不存在该类则创建,否则直接返回
if(_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
}

多线程时的解决方案

上面的单例模式在单线程中使用是没有任何问题的,但是在多线程下会出现两个线程同时访问该类属性的情况,出现这种情况时,两个线程在执行到条件(_instance == null)时都会判断为真,导致两个线程都会创建该类的实例,这样就违背了只有一个实例的初衷了,这种情况的解决的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Singleton
{
//创建一个静态变量来保存类的实例
private static Singleton _instance;

//定义一个标识确保线程同步
private static readonly object locker = new object();

//定义私有构造函数,使外界不能使用new创建该类实例
private Singleton()
{
}

//定义一个公有的方法或属性为外部提供一个访问该类的全局访问点
//此处以属性为例
public static Singleton Instance
{
get
{
//当第一个线程运行到这里时,此时会对locker对象“加锁”
//当第二个线程运行该方法时,首先检测到locker对象为加锁状态,该线程就会挂起等待第一个线程解锁
//lock语句运行完之后就会对该对象进行“解锁”
lock(locker)
{
//如果不存在该类则创建,否则直接返回
if(_instance == null)
{
_instance = new Singleton();
}
}
return _instance;
}
}
}