单例模式:保证进程中,某个类只有一个实例。
要做到单例,必须要解决一下几个问题:
1 怎么保证呢?怎么样强制保证呢?
单例模式的三部曲:A.构造方法私有化,防止外部对它实例化。B. 静态对象、方法的申明。C.判断对象是否为空,为空则创建对象,最后返回对象。
示例代码:
1 /// <summary> 2 /// 单例类:一个构造对象很耗时耗资源类型 3 /// 懒汉式单例模式 4 /// </summary> 5 public class Singleton 6 { 7 /// <summary> 8 /// 构造函数耗时耗资源 9 /// </summary> 10 private Singleton() 11 { 12 18 Console.WriteLine("{0}被构造一次", this.GetType().Name); 19 } 20 /// <summary> 21 /// 3 全局唯一静态 重用这个变量 22 /// </summary> 23 private static volatile Singleton _Singleton = null; 24 //volatile 促进线程安全 让线程按顺序操作 25 private static readonly object Singleton_Lock = new object(); 26 /// <summary> 27 /// 2 公开的静态方法提供对象实例 28 /// </summary> 29 /// <returns></returns> 30 public static Singleton CreateInstance() 31 { 40 if (_Singleton == null)//保证只实例化一次 41 { 42 _Singleton = new Singleton(); 43 }46 return _Singleton; 47 } 65 }
调用:Console.WriteLine(object.ReferenceEquals(singleton1, singleton2)); 控制台输出ture,则说明这个两个对象是同一个对象。
1 static void Main(string[] args) 2 { 3 try 4 { 5 //为什么要有单例设计模式? 6 //构造对象耗时耗资源,很多地方都需要去new, 这个方法 其他方法 其他类 7 //想避免重复构造,公开静态字段 8 //1 提前构造 2 没办法保证都是用这个(其他人还是new了一下) 9 //Singleton singleton = new Singleton(); 10 //singleton.Show(); 11 12 //{ 13 // ////保证进程中,某个类只有一个实例 14 // ////1 构造函数私有化 避免别人还去new 15 // ////2 公开的静态方法提供对象实例 16 // ////3 初始化一个静态字段用于返回 保证全局都是这一个 17 Singleton singleton1 = Singleton.CreateInstance(); 18 Singleton singleton2 = Singleton.CreateInstance(); 19 Singleton singleton3 = Singleton.CreateInstance(); 20 Console.WriteLine(object.ReferenceEquals(singleton1, singleton2)); 21 Console.WriteLine(object.ReferenceEquals(singleton3, singleton2)); 22 } 23 catch (Exception ex) 24 { 25 Console.WriteLine(ex.Message); 26 } 27 Console.Read(); 28 }
多线程调用:控制台输出 对象被构造了五次,这时候就会发现结果和我们预期的不一样了。
{ for (int i = 0; i < 5; i++) { Task.Run(() =>//启动线程完成--5个线程并发执行,同时去执行这个方法 { Singleton singleton1 = Singleton.CreateInstance(); singleton1.Show(); });
} }
对上述单例模式进行改造,要求是满足多线程使用场景、单线程使用场景。
1 /// <summary> 2 /// 单例类:一个构造对象很耗时耗资源类型 3 /// 懒汉式单例模式 4 /// </summary> 5 public class Singleton 6 { 7 /// <summary> 8 /// 构造函数耗时耗资源 9 /// </summary> 10 private Singleton() 11 { 12 long lResult = 0; 13 for (int i = 0; i < 10000000; i++) 14 { 15 lResult += i; 16 } 17 Thread.Sleep(2000); 18 Console.WriteLine("{0}被构造一次", this.GetType().Name); 19 } 20 /// <summary> 21 /// 3 全局唯一静态 重用这个变量 22 /// </summary> 23 private static volatile Singleton _Singleton = null; 24 //volatile 促进线程安全 让线程按顺序操作 25 private static readonly object Singleton_Lock = new object(); 26 /// <summary> 27 /// 2 公开的静态方法提供对象实例 28 /// </summary> 29 /// <returns></returns> 30 public static Singleton CreateInstance() 31 { 32 if (_Singleton == null)//是_Singleton已经被初始化之后,就不要进入锁等待了 33 { 34 lock (Singleton_Lock) 35 //保证任意时刻只有一个线程进入lock范围 36 //也限制了并发,尤其是_Singleton已经被初始化之后 37 { 38 //Thread.Sleep(1000); 39 //Console.WriteLine("等待锁1s之后才继续。。。"); 40 if (_Singleton == null)//保证只实例化一次 41 { 42 _Singleton = new Singleton(); 43 } 44 } 45 } 46 return _Singleton; 47 } 48 49 //既然是单例,大家用的是同一个对象,用的是同一个方法,那还会并发吗 还有线程安全问题吗? 50 public int iTotal = 0; 51 public void Show() 52 { 53 //lock (Singleton_Lock) 54 //{ 55 this.iTotal++; 56 //} 57 } 58 59 public static void Test() 60 { 61 Console.WriteLine("Test1"); 62 Console.WriteLine(_Singleton.iTotal); 63 } 64 65 }
多线程调用:
Thread.Sleep(10000); Console.WriteLine("第一波多线程后,再度来请求实例"); for (int i = 0; i < 5; i++) { Task.Run(() =>//启动线程完成--5个线程并发执行,同时去执行这个方法 { Singleton singleton1 = Singleton.CreateInstance(); singleton1.Show(); }); }
注意:多线程中单例的使用 最主要的是 双重if判断,和加锁。内层if判断的作用是第一次调用实例化一次,下次在调用的时候不用实例化对象。加锁的作用说保证线程安全(加锁后就变成的单线程,第一个线程没有处理完,后面的线程都需要等待,这个时候就使用多线程就失去了意义),为了解决加锁后编程单线程问题,所以在锁外面在加一层If判断,多线程操作程序会并发执行,当第一个线程已经创建了对象 ,其他线程进入方法是检测到对象已经不为空后,直接返回对象。这样就不用等待,提高了程序的效率。
饿汉式单例
1 /// <summary> 2 /// 单例类:一个构造对象很耗时耗资源类型 3 /// 4 /// 饿汉式 5 /// </summary> 6 public class SingletonSecond 7 { 8 /// <summary> 9 /// 1 构造函数耗时耗资源 10 /// </summary> 11 private SingletonSecond() 12 { 13 long lResult = 0; 14 for (int i = 0; i < 10000000; i++) 15 { 16 lResult += i; 17 } 18 Thread.Sleep(1000); 19 Console.WriteLine("{0}被构造一次", this.GetType().Name); 20 } 21 /// <summary> 22 /// 静态构造函数:由CLR保证,程序第一次使用这个类型前被调用,且只调用一次 23 /// 24 /// 检测,初始化 25 /// 写日志功能的文件夹检测 26 /// XML配置文件 27 /// </summary> 28 static SingletonSecond() 29 { 30 _SingletonSecond = new SingletonSecond(); 31 Console.WriteLine("SingletonSecond 被启动"); 32 } 33 34 35 private static SingletonSecond _SingletonSecond = null; 36 public static SingletonSecond CreateInstance() 37 { 38 return _SingletonSecond; 39 }//饿汉式 只要使用类就会被构造 40 41 /// <summary> 42 /// 原型模式:解决对象重复创建的问题 43 /// 通过MemberwiseClone来clone新对象,内存操作,直接复制的,避免重复创建 44 /// </summary> 45 /// <returns></returns> 46 public static SingletonSecond CreateInstancePrototype() 47 { 48 SingletonSecond second = (SingletonSecond)_SingletonSecond.MemberwiseClone(); 49 return second; 50 } 51 52 public static void Test() 53 { 54 Console.WriteLine("Test2"); 55 } 56 57 58 59 public int iTotal = 0; 60 public void Show() 61 { 62 this.iTotal++; 63 } 64 65 }
第三种单例模式 :
1 /// <summary> 2 /// 单例类:一个构造对象很耗时耗资源类型 3 /// 饿汉式 4 /// </summary> 5 public class SingletonThird 6 { 7 /// <summary> 8 /// 构造函数耗时耗资源 9 /// </summary> 10 private SingletonThird() 11 { 12 long lResult = 0; 13 for (int i = 0; i < 10000000; i++) 14 { 15 lResult += i; 16 } 17 Thread.Sleep(1000); 18 Console.WriteLine("{0}被构造一次", this.GetType().Name); 19 } 20 21 /// <summary> 22 /// 静态字段:在第一次使用这个类之前,由CLR保证,初始化且只初始化一次 23 /// 这个比今天构造函数还早 24 /// </summary> 25 private static SingletonThird _SingletonThird = new SingletonThird();//打印个日志 26 public static SingletonThird CreateInstance() 27 { 28 return _SingletonThird; 29 }//饿汉式 只要使用类就会被构造
34 public void Show() 35 { 36 Console.WriteLine("这里是{0}.Show", this.GetType().Name); 37 } 38 39 }
辅导费