设计模式:单件模式(Singleton Pattern)
作者:TerryLee 创建于:2005-12-09 出处:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html 收录于:2013-03-01
模型图
意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
三种实现
1.简单实现
1 using System; 2 class Singleton 3 { 4 private static Singleton instance; 5 protected Singleton() { } 6 public static Singleton Instance() 7 { 8 if (instance == null) 9 instance = new Singleton(); 10 return instance; 11 } 12 } 13 public class Client 14 { 15 public static void Main() 16 { 17 Singleton s1 = Singleton.Instance(); 18 Singleton s2 = Singleton.Instance(); 19 if (s1 == s2) 20 Console.WriteLine("The same instance"); 21 Console.Read(); 22 } 23 }
结果:The same instance
也可以用属性去的实例,看以下代码(C#类成员默认修饰符private):
1 public sealed class Singleton 2 { 3 static Singleton instance=null; 4 Singleton() 5 { 6 } 7 public static Singleton Instance 8 { 9 get 10 { 11 if (instance==null) 12 { 13 instance = new Singleton(); 14 } 15 return instance; 16 } 17 } 18 } 19 20 public class Client 21 { 22 public static void Main() 23 { 24 Singleton s1 = Singleton.Instance; 25 Singleton s2 = Singleton.Instance; 26 if (s1 == s2) 27 Console.WriteLine("The same instance"); 28 Console.Read(); 29 } 30 }
这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。
2.安全的线程
1 using System; 2 public sealed class Singleton 3 { 4 static Singleton instance=null; 5 static readonly object padlock = new object(); 6 Singleton() 7 { 8 } 9 public static Singleton Instance 10 { 11 get 12 { 13 lock (padlock) 14 { 15 if (instance==null) 16 { 17 instance = new Singleton(); 18 } 19 return instance; 20 } 21 } 22 } 23 }
这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时(instence == null)为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。
但是无论instance是否为空都加锁,影响性能,在lock前加一条判断
if (instance==null)
3.静态初始化
1 using System; 2 public sealed class Singleton 3 { 4 static readonly Singleton instance=new Singleton(); 5 Singleton() 6 { 7 } 8 public static Singleton Instance 9 { 10 get 11 { 12 return instance; 13 } 14 } 15 }
在此实现中,将在第一次引用类的任何成员时创建实例。
这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。
3.1延迟初始化
1 using System; 2 public sealed class Singleton 3 { 4 internal Singleton()//注意:这里加了internal修饰,否则无法创建 5 { 6 } 7 public static Singleton Instance 8 { 9 get 10 { 11 return Nested.instance; 12 } 13 } 14 15 class Nested 16 { 17 static Nested() 18 { 19 } 20 internal static readonly Singleton instance = new Singleton(); 21 } 22 }
实现要点
1 Singleton模式是限制而不是改进类的创建。
2 Singleton类中的实例构造器可以设置为Protected以允许子类派生。
3 Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
4 Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。
5 Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。
6
理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
7
可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。
优点
1 实例控制:Singleton
会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
2 灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
缺点
开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。
完整示例:计数器
using System; using System.Text; using System.Threading; namespace SigletonPattern.SigletonCounter { /**//// <summary> /// 功能:简单计数器的单件模式 /// </summary> public class CountSigleton { static CountSigleton uniCounter = new CountSigleton(); /**////存储计数值 private int totNum = 0; private CountSigleton() { /**////线程延迟2000毫秒 Thread.Sleep(2000); } static public CountSigleton Instance() { return uniCounter; } /**////计数加1 public void Add() { totNum ++; } /**////获得当前计数值 public int GetCounter() { return totNum; } } //// <summary> /// 功能:创建一个多线程计数的类 /// </summary> public class CountMutilThread { static readonly object lockpad = new object(); public CountMutilThread() { } /**//// <summary> /// 线程工作 /// </summary> public static void DoSomeWork() { /**////构造显示字符串 string results = ""; /**////创建一个Sigleton实例 CountSigleton MyCounter = CountSigleton.Instance(); /**////循环调用四次 for(int i=1;i<5;i++) { lock (lockpad)//必须加上,否则这段代码没有线程安全性。 { /**////开始计数 MyCounter.Add(); results +="线程"; results += Thread.CurrentThread.Name.ToString() + "——〉"; results += "当前的计数:"; results += MyCounter.GetCounter().ToString(); results += "\n"; Console.WriteLine(results); /**////清空显示字符串 results = ""; } } } public void StartMain() { Thread thread0 = Thread.CurrentThread; thread0.Name = "Thread 0"; Thread thread1 =new Thread(new ThreadStart(DoSomeWork)); thread1.Name = "Thread 1"; Thread thread2 =new Thread(new ThreadStart(DoSomeWork)); thread2.Name = "Thread 2"; thread1.Start(); thread2.Start(); /**////线程0也只执行和其他线程相同的工作 DoSomeWork(); } } /// <summary> /// 功能:实现多线程计数器的客户端 /// </summary> public class CountClient { public static void Main(string[] args) { CountMutilThread cmt = new CountMutilThread(); cmt.StartMain(); Console.ReadLine(); } } }
结果:
说明:这里线程顺序无所谓,我们可以看到计数器是递增的,没有因为线程的不同而改变,因此,可以得知只创建了一个实例。
参考
【1】http://terrylee.cnblogs.com/archive/2005/12/09/293509.html
【2】http://www.cnblogs.com/zhenyulu/articles/37246.html