保证一个类仅有一个实例,并提供一个该实例的全局访问点。 ——《设计模式》
单例模式的概念很简单,下面以C#语言为例子,列出常见单例写法的优缺点。
1、简单实现
[csharp] view plain copy
1. public sealed class Singleton
2. {
3. static Singleton instance = null ;
4.
5. public void Show()
6. {
7. Console.WriteLine( "instance function");
8. }
9. private Singleton()
10. {
11. }
12.
13. public static Singleton Instance
14. {
15. get
16. {
17. if (instance == null )
18. {
19. instance = new Singleton();
20. }
21. return instance;
22. }
23. }
24. }
评注:
对于线程来说不安全
单线程中已满足要求
优点:
由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能
直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton 。 2
、线程的安全
[csharp] view plain copy
1. public sealed class Singleton
2. {
3. static Singleton instance = null ;
4. private static readonly object padlock = new object ();
5.
6. private Singleton()
7. {
8. }
9.
10. public static Singleton Instance
11. {
12. get
13. {
14. lock (padlock)
15. {
16. if (instance == null )
17. {
18. instance = new Singleton();
19. }
20. }
21.
22. return instance;
23. }
24. }
25. }
评注:
同一个时刻加了锁的那部分程序只有一个线程可以进入
对象实例由最先进入的那个线程创建
后来的线程在进入时(instence == null)为假,不会再去创建对象实例
增加了额外的开销,损失了性能
3、双重锁定
[csharp] view plain copy
1. public sealed class Singleton
2. {
3. static Singleton instance = null ;
4. private static readonly object padlock = new object ();
5.
6. private Singleton()
7. {
8. }
9.
10. public static Singleton Instance
11. {
12. get
13. {
14. if (instance == null )
15. {
16. lock (padlock)
17. {
18. if (instance == null )
19. {
20. instance = new Singleton();
21. }
22. }
23. }
24. return instance;
25. }
26. }
27. }
评注:
多线程安全
线程不是每次都加锁
允许实例化延迟到第一次访问对象时发生
4、静态初始化
[csharp] view plain copy
1. public sealed class Singleton
2. {
3. private static readonly Singleton instance = null ;
4. static Singleton()
5. {
6. instance = new Singleton();
7. }
8. private Singleton()
9. {
10. }
11. public static Singleton Instance
12. {
13. get
14. {
15. return instance;
16. }
17. }
18. }
评注:
依赖公共语言运行库负责处理变量初始化
公共静态属性为访问实例提供了一个全局访问点
对实例化机制的控制权较少(.NET代为实现)
静态初始化是在 .NET 中实现 Singleton 的首选方法
小注:
静态构造函数既没有访问修饰符,C#会自动把他们标记为private ,之所以必须标记为private ,
是为了阻止开发人员写的代码调用它,对它的调用总是由CLR 负责的。
5、延迟初始化
[csharp] view plain copy 1. public sealed class Singleton
2. {
3. private Singleton()
4. {
5. }
6. public static Singleton Instance
7. {
8. get
9. {
10. return Nested.instance;
11. }
12. }
13.
14. public static void Hello()
15. {
16. }
17.
18. private class Nested
19. {
20. internal static readonly Singleton instance = null ;
21. static Nested()
22. {
23. instance = new Singleton();
24. }
25. }
26. }
评注:
初始化工作由Nested 类的一个静态成员来完成,这样就实现了延迟初始化。
由于静态函数的调用时机,是在类被实例化或者静态成员被调用的时候进行调用,并且是由.net 框架来调用静态构造函数来初始化静态成员变量, 所以,如果按照写法四来写,再调用Hello 方法的时候,就会实例化出来Singleton 实例,这不是我们想看到的,因为我们有可能只是想用Hello 方法,而不是别的。
注意事项:
1、Singleton
模式中的实例构造器可以设置为protected 以允许子类派生。
2、Singleton 模式一般不要支持ICloneable 接口,因为这可能会导致多个对象实例,与Singleton 模式的初衷违背。
3、Singleton 模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton 模式的初衷违背。
4、Singletom 模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。
总结:
1、Singleton 模式是限制而不是改进类的创建。
2、理解和扩展Singleton 模式的核心是“如何控制用户使用new 对一个类的构造器的任意调用”。
3、可以很简单的修改一个Singleton ,使它有少数几个实例,这样做是允许的而且是有意义的。