10-单例模式
1. 单例模式的作用
保证多个进程内,(即使多线程访问)该对象的实例只有一个,而且只会被创建一次。
2. 单例模式什么时候用?
该对象的构造函数非常耗时,实例化一次该对象,需要非常长的时间,这样如果每次都去创建,会消耗很多资源。
3. 单例模式的代价
private static Singleton _Singleton = null; 该静态对象不会被回收,会常驻内存。
4. 特别注意
单例只保证了实例的唯一,并不能保证实例中的方法或变量也唯一,所以单例中的方法或变量,在多线程面前,仍然可能是不安全的。
5. 单例模式的三种写法
双if+Lock(懒汉模式)、静态构造函数(饿汉模式)、静态变量(饿汉模式)。
(1). 双if+Lock
下面重点介绍“双if+Lock”由单线程→多线程的演变过程。
1 public class SOne 2 { 3 /// <summary> 4 /// 模拟耗时的构造函数 5 /// </summary> 6 public SOne() 7 { 8 long result = 0; 9 for (int i = 0; i < 1000000; i++) 10 { 11 result += i; 12 } 13 Thread.Sleep(1000); 14 Console.WriteLine("{0}被构造...", this.GetType().Name); 15 } 16 17 private static SOne _Sone = null; 18 private static object SoneLock = new object(); 19 20 /// <summary> 21 /// 单线程内单例模式 22 /// </summary> 23 /// <returns></returns> 24 public static SOne CreateIntance1() 25 { 26 //我们发现:在多线程中,实例创建了不止一次,原因是多个线程同时进入了 27 if (_Sone == null) 28 { 29 _Sone = new SOne(); 30 } 31 return _Sone; 32 } 33 /// <summary> 34 /// 多线程的单例模式(安全+但是费时,费在锁上了) 35 /// </summary> 36 /// <returns></returns> 37 public static SOne CreateIntance2() 38 { 39 //给下面的语句整体加上锁,即使多线程,也需要一个一个的进入锁中进行判断,不存在同时进入创建实例内部的情况了。 40 //缺点:同时带来一个问题,所有的线程必须等着前面一个锁完成后,方可进入,即使实例已经创建完了,也需要等前面的锁完成,这样多个线程耗时就耗在等锁上了 41 lock (SoneLock) 42 { 43 Console.WriteLine("我进入锁了,后面的要等我一下哦"); 44 Thread.Sleep(1000); //模拟锁操作耗时 45 if (_Sone == null) 46 { 47 _Sone = new SOne(); 48 } 49 } 50 return _Sone; 51 } 52 53 /// <summary> 54 /// 多线程的单例模式(安全且不耗时) 55 /// </summary> 56 /// <returns></returns> 57 public static SOne CreateIntance3() 58 { 59 //外层再加一层if,在进锁前就可以判断了,如果前面已经有实例创建好了,那么以后的就不需要等锁了,但是也可能存在多线程同时进入第一个if,那么同时进入的也要等一下哦。 60 if (_Sone==null) 61 { 62 lock (SoneLock) 63 { 64 Console.WriteLine("我进入锁了,后面的要等我一下哦"); 65 //Thread.Sleep(1000); //模拟锁操作耗时 66 if (_Sone == null) 67 { 68 _Sone = new SOne(); 69 } 70 } 71 } 72 return _Sone; 73 } 74 }
(2). 静态构造函数
1 public class STwo 2 { 3 /// <summary> 4 /// 模拟耗时的构造函数 5 /// </summary> 6 private STwo() 7 { 8 long result = 0; 9 for (int i = 0; i < 1000000; i++) 10 { 11 result += i; 12 } 13 Thread.Sleep(1000); 14 Console.WriteLine("{0}被构造...", this.GetType().Name); 15 } 16 17 private static STwo _STwo = null; 18 /// <summary> 19 /// 静态的构造函数:只能有一个,且是无参数的 20 /// 由CLR保证,只有在程序第一次使用该类之前被调用,而且只能调用一次 21 /// </summary> 22 static STwo() 23 { 24 _STwo = new STwo(); 25 } 26 27 public static STwo CreateIntance() 28 { 29 return _STwo; 30 } 31 }
(3). 静态变量
1 public class SThird 2 { 3 /// <summary> 4 /// 模拟耗时的构造函数 5 /// </summary> 6 private SThird() 7 { 8 long result = 0; 9 for (int i = 0; i < 1000000; i++) 10 { 11 result += i; 12 } 13 Thread.Sleep(1000); 14 Console.WriteLine("{0}被构造...", this.GetType().Name); 15 } 16 /// <summary> 17 /// 静态变量:由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次 18 /// </summary> 19 private static SThird _SThird = new SThird(); 20 21 public static SThird CreateIntance() 22 { 23 return _SThird; 24 } 25 }