单例模式的目的是:
确保类只有一个实例并提供全局访问。
实现
首先是最简单的单例模式的代码:
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
| namespace DesignPattern.SingletonPattern { public class Singleton { private static Singleton uniqueInstance;
private Singleton() { }
public static Singleton getInstance() { if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; } } }
|
Singleton
的构造方法被定义成了 private
,这代表无法在 Singleton
类的外部实例化 Singleton
类。
getInstance
方法被设置为 static
,这样在 Singleton
类外部即使不实例化 Singleton
类也可以通过 Singleton.getInstance()
调用此方法。
这样,只要在 getInstance
方法中进行简单的判断,就可以实现从始至终只有一个 Singleton
类的实例。
多线程
然而,在多线程时就会出现问题:两个线程在运行时,可能会同时出现 (uniqueInstance == null)
返回为真的情况,这就导致会实例化一个以上的 Singleton
类的情况出现。
所以,我们需要将其变成同步方法,在 C# 中可以用以下两种方法实现:
第一是使用 [MethodImpl(MethodImplOptions.Synchronized)]
属性:
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
| using System.Runtime.CompilerServices;
namespace DesignPattern.SingletonPattern { public class Singleton { private static Singleton uniqueInstance;
private Singleton() { }
[MethodImpl(MethodImplOptions.Synchronized)] public static Singleton getInstance() { if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; } } }
|
第二种是使用 lock
:
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 35 36 37
| namespace DesignPattern.SingletonPattern { public class Singleton { private static Singleton uniqueInstance;
private static readonly object locker = new object();
private Singleton() { }
public static Singleton getInstance() { lock (locker) { if (uniqueInstance == null) uniqueInstance = new Singleton(); } return uniqueInstance; } } }
|
上面两种方法都可以解决在单例模式下多线程的问题,但是如果你在程序中频繁用到 Singleton.getInstance()
将会大大的使性能降低,因为同步一个方法会使程序执行效率下降,所以我们可以在这种情况下使用如下方法:
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
| namespace DesignPattern.SingletonPattern { public class Singleton { private static Singleton uniqueInstance = new Singleton();
private Singleton() { }
public static Singleton getInstance() { return uniqueInstance; } } }
|
可以看到,我们在声明 uniqueInstance
的时候直接实例化了 Singleton
类。
这样在程序开始的时候这个唯一实例就已经存在了。避免了同步方法,也就解决了多线程的问题。
注意的点
在多线程问题的解决上,需要选择适合的方案来实现单例模式,如果单例实例在程序中并不是经常使用,而且实例化的时候开销很大的话,可以使用同步 getInstance
方法来解决多线程问题,如果单例实例需要在程序中频繁使用,那就可以在声明 uniqueInstance
变量的时间就进行实例化,这样就可以解决同步 getInstance
方法时带来的性能下降的问题。
如果你有多个构造函数(重载),就需要注意会产生多个实例导致单例失效。
代码
SingletonPattern