单例模式: 保证进程中, 某个类只有一个实例.
要做到单例, 必须要解决一下几个问题:
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, 则说明这个两个对象是同一个对象.
- static void Main(string[] args)
- {
- try
- {
- // 为什么要有单例设计模式?
- // 构造对象耗时耗资源, 很多地方都需要去 new, 这个方法 其他方法 其他类
- // 想避免重复构造, 公开静态字段
- //1 提前构造 2 没办法保证都是用这个 (其他人还是 new 了一下)
- //Singleton singleton = new Singleton();
- //singleton.Show();
- //{
- // //// 保证进程中, 某个类只有一个实例
- // ////1 构造函数私有化 避免别人还去 new
- // ////2 公开的静态方法提供对象实例
- // ////3 初始化一个静态字段用于返回 保证全局都是这一个
- Singleton singleton1 = Singleton.CreateInstance();
- Singleton singleton2 = Singleton.CreateInstance();
- Singleton singleton3 = Singleton.CreateInstance();
- Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
- Console.WriteLine(object.ReferenceEquals(singleton3, singleton2));
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- Console.Read();
- }
多线程调用: 控制台输出 对象被构造了五次, 这时候就会发现结果和我们预期的不一样了.
- {
- for (int i = 0; i <5; i++)
- {
- Task.Run(() =>// 启动线程完成 --5 个线程并发执行, 同时去执行这个方法
- {
- Singleton singleton1 = Singleton.CreateInstance();
- singleton1.Show();
- });
- }
- }
对上述单例模式进行改造, 要求是满足多线程使用场景, 单线程使用场景.
- /// <summary>
- /// 单例类: 一个构造对象很耗时耗资源类型
- /// 懒汉式单例模式
- /// </summary>
- public class Singleton
- {
- /// <summary>
- /// 构造函数耗时耗资源
- /// </summary>
- private Singleton()
- {
- long lResult = 0;
- for (int i = 0; i <10000000; i++)
- {
- lResult += i;
- }
- Thread.Sleep(2000);
- Console.WriteLine("{0} 被构造一次", this.GetType().Name);
- }
- /// <summary>
- /// 3 全局唯一静态 重用这个变量
- /// </summary>
- private static volatile Singleton _Singleton = null;
- //volatile 促进线程安全 让线程按顺序操作
- private static readonly object Singleton_Lock = new object();
- /// <summary>
- /// 2 公开的静态方法提供对象实例
- /// </summary>
- /// <returns></returns>
- public static Singleton CreateInstance()
- {
- if (_Singleton == null)// 是_Singleton 已经被初始化之后, 就不要进入锁等待了
- {
- lock (Singleton_Lock)
- // 保证任意时刻只有一个线程进入 lock 范围
- // 也限制了并发, 尤其是_Singleton 已经被初始化之后
- {
- //Thread.Sleep(1000);
- //Console.WriteLine("等待锁 1s 之后才继续...");
- if (_Singleton == null)// 保证只实例化一次
- {
- _Singleton = new Singleton();
- }
- }
- }
- return _Singleton;
- }
- // 既然是单例, 大家用的是同一个对象, 用的是同一个方法, 那还会并发吗 还有线程安全问题吗?
- public int iTotal = 0;
- public void Show()
- {
- //lock (Singleton_Lock)
- //{
- this.iTotal++;
- //}
- }
- public static void Test()
- {
- Console.WriteLine("Test1");
- Console.WriteLine(_Singleton.iTotal);
- }
- }
多线程调用:
- Thread.Sleep(10000);
- Console.WriteLine("第一波多线程后, 再度来请求实例");
- for (int i = 0; i <5; i++)
- {
- Task.Run(() =>// 启动线程完成 --5 个线程并发执行, 同时去执行这个方法
- {
- Singleton singleton1 = Singleton.CreateInstance();
- singleton1.Show();
- });
- }
注意: 多线程中单例的使用 最主要的是 双重 if 判断, 和加锁. 内层 if 判断的作用是第一次调用实例化一次, 下次在调用的时候不用实例化对象. 加锁的作用说保证线程安全 (加锁后就变成的单线程, 第一个线程没有处理完, 后面的线程都需要等待, 这个时候就使用多线程就失去了意义), 为了解决加锁后编程单线程问题, 所以在锁外面在加一层 If 判断, 多线程操作程序会并发执行, 当第一个线程已经创建了对象 , 其他线程进入方法是检测到对象已经不为空后, 直接返回对象. 这样就不用等待, 提高了程序的效率.
饿汉式单例
- /// <summary>
- /// 单例类: 一个构造对象很耗时耗资源类型
- ///
- /// 饿汉式
- /// </summary>
- public class SingletonSecond
- {
- /// <summary>
- /// 1 构造函数耗时耗资源
- /// </summary>
- private SingletonSecond()
- {
- long lResult = 0;
- for (int i = 0; i <10000000; i++)
- {
- lResult += i;
- }
- Thread.Sleep(1000);
- Console.WriteLine("{0} 被构造一次", this.GetType().Name);
- }
- /// <summary>
- /// 静态构造函数: 由 CLR 保证, 程序第一次使用这个类型前被调用, 且只调用一次
- ///
- /// 检测, 初始化
- /// 写日志功能的文件夹检测
- /// xml 配置文件
- /// </summary>
- static SingletonSecond()
- {
- _SingletonSecond = new SingletonSecond();
- Console.WriteLine("SingletonSecond 被启动");
- }
- private static SingletonSecond _SingletonSecond = null;
- public static SingletonSecond CreateInstance()
- {
- return _SingletonSecond;
- }// 饿汉式 只要使用类就会被构造
- /// <summary>
- /// 原型模式: 解决对象重复创建的问题
- /// 通过 MemberwiseClone 来 clone 新对象, 内存操作, 直接复制的, 避免重复创建
- /// </summary>
- /// <returns></returns>
- public static SingletonSecond CreateInstancePrototype()
- {
- SingletonSecond second = (SingletonSecond)_SingletonSecond.MemberwiseClone();
- return second;
- }
- public static void Test()
- {
- Console.WriteLine("Test2");
- }
- public int iTotal = 0;
- public void Show()
- {
- this.iTotal++;
- }
- }
第三种单例模式 :
- /// <summary>
- /// 单例类: 一个构造对象很耗时耗资源类型
- /// 饿汉式
- /// </summary>
- public class SingletonThird
- {
- /// <summary>
- /// 构造函数耗时耗资源
- /// </summary>
- private SingletonThird()
- {
- long lResult = 0;
- for (int i = 0; i <10000000; i++)
- {
- lResult += i;
- }
- Thread.Sleep(1000);
- Console.WriteLine("{0} 被构造一次", this.GetType().Name);
- }
- /// <summary>
- /// 静态字段: 在第一次使用这个类之前, 由 CLR 保证, 初始化且只初始化一次
- /// 这个比今天构造函数还早
- /// </summary>
- private static SingletonThird _SingletonThird = new SingletonThird();// 打印个日志
- public static SingletonThird CreateInstance()
- {
- return _SingletonThird;
- }// 饿汉式 只要使用类就会被构造
- public void Show()
- {
- Console.WriteLine("这里是 {0}.Show", this.GetType().Name);
- }
- }
辅导费
来源: https://www.cnblogs.com/super-xi-xi/p/10308588.html