来自:CCNetCore社区,一个关注.Netcore领域的社区团队。
为了保证对象的唯一性,还可提高程序的性能。
单例模式有3种实现方式
每次创建时进行判断(需要双重判断)
将对象放入静态构造函数种实例
将对象放入静态字段中
注意:单例模式并不能保证多线程的安全性,一万次并发中可能会错误50次左右
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SingletonPattern { /// <summary> /// 1 单例模式(单线程和多线程) /// 2 单例模式的三种写法 /// 3 单例模式的优缺点 /// 4 深度探讨单例模式应用场景 /// /// 献给在座诸位小伙伴儿,点个赞! /// 今天用面试的形式来完成教学!(源自于老师亲身面试经历4年) /// 1 写一个单例模式 /// 单例模式:保证进程中,类型只有一个实例 /// a) 构造函数私有化,防止他人实例化 /// b) 对外提供一个获取实例的途径,公开的静态方法 /// c) 返回共用一个静态字段 /// 60分---70分---80分---90分 /// 怎么高分? /// 尽量展示自己擅长的,会几种就多写几种 /// 面试时间就那么长,多花点没错 /// /// (面试是个随机的 循序渐进的 发散式的过程) /// /// 2 单例有什么用,为什么要用单例,什么时候用单例? /// 节省资源? 对象常驻内存,重用对象 /// 即时申请--使用--释放 /// 学完单例,就恨不得把全部类型都搞成单例,这是不对的 /// 同一个实例,不能解决多线程并发问题!!! /// /// 单例就是程序只需要这个对象实例化一次,只允许实例化 /// 数据库连接池:数据库连接--非托管资源--申请/释放消耗性能 /// 池化资源--内置10个链接---使用来拿,用完放回来--避免重复申请和销毁 /// ---控制链接数量 /// 线程池 流水号生成器 配置文件读取 IOC容器实例 /// /// 3 继续提问,流水号生成器怎么实现的 为啥单例 性能如何。。。 /// /// /// 老师今天做了个新尝试,把面试+教学融为一炉,还夹杂一些面试心得, /// 如果同学们觉得挺有收获的 大开眼界 受到启发的, /// 觉得Eleven讲的不错,真的很有才,刷个666,给老师点个赞 /// /// </summary> class Program { static void Main(string[] args) { try { ////5个不同的Singleton实例 //for (int i = 0; i < 5; i++) //{ // Singleton singleton = new Singleton(); // singleton.Show(); //} //for (int i = 0; i < 5; i++) //{ // Singleton singleton = Singleton.CreateInstance(); // singleton.Show(); //} //for (int i = 0; i < 5; i++) //{ // Task.Run(() =>//启动一个线程执行这些动作;可以认为5个同时执行 // { // Singleton singleton = Singleton.CreateInstance(); // singleton.Show(); // }); //} //{ // for (int i = 0; i < 5; i++) // { // SingletonSecond singletonSecond = SingletonSecond.CreateInstance(); // } //} //for (int i = 0; i < 5; i++) //{ // Task.Run(() => // { // SingletonThird singletonThird = SingletonThird.CreateInstance(); // }); //} List<Task> taskList = new List<Task>(); for (int i = 0; i < 10000; i++) { taskList.Add(Task.Run(() => { Singleton singleton = Singleton.CreateInstance(); singleton.Invoke(); })); } Task.WaitAll(taskList.ToArray());//等待1w个Task都完成 { Singleton singleton = Singleton.CreateInstance(); Console.WriteLine(singleton.Num); //Num:0 1 10000 还是其他 } //0 大错特错! 全局都是一个实例 //10000 单例知道 线程安全问题 } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read(); } } //影 单例对象能克隆吗 //怎么保证是10000呢 }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace SingletonPattern { /// <summary> /// 单例类:一个构造对象很耗时耗资源类型 /// /// 懒汉式 /// </summary> public sealed class Singleton { /// <summary> /// 对象会持有资源 /// </summary> private List<string> ConnList = new List<string>() { "这里是数据库连接1", "这里是数据库连接2", "这里是数据库连接3", "这里是数据库连接4", "这里是数据库连接5"//一些其他资源 }; /// <summary> /// 构造函数耗时耗资源 /// </summary> private Singleton() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(1000); Console.WriteLine($"{this.GetType().Name}被构造一次"); } private static Singleton _Singleton = null; private static readonly object Singleton_Lock = new object(); public static Singleton CreateInstance() { //开始多线程初始化---lock锁定--线程请求锁--开始判断创建 //以上多线程都结束后---再来多个线程请求呢?--都需要等待锁 //--拿到锁--进去判断--不为空--返回对象--。。。。 //有点浪费,不需要等待锁,直接判断就行了 if (_Singleton == null) { lock (Singleton_Lock)//可以保证任意时刻只有一个线程进入,其他线程等待 { if (_Singleton == null)//这个判断不能去掉,保证只初始化一次 { _Singleton = new Singleton(); } } } return _Singleton; } public static void DoNothing() { } public void Show() { Console.WriteLine($"这里是{this.GetType().Name}.Show"); } public int Num = 0;//不是线程安全的 10000个线程同时来+1 结果一定小于10000 /// <summary> /// 一个操作,有个计数 /// </summary> public void Invoke() { this.Num++; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace SingletonPattern { /// <summary> /// 饿汉式 /// </summary> public sealed class SingletonSecond { /// <summary> /// 对象会持有资源 /// </summary> private List<string> ConnList = new List<string>() { "这里是数据库连接1", "这里是数据库连接2", "这里是数据库连接3", "这里是数据库连接4", "这里是数据库连接5"//一些其他资源 }; /// <summary> /// 构造函数耗时耗资源 /// </summary> private SingletonSecond() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(1000); Console.WriteLine($"{this.GetType().Name}被构造一次"); } private static SingletonSecond _SingletonSecond = null; /// <summary> /// 静态构造函数:由CLR保证,在第一次使用到这个类型之前,自动被调用且只调用一次 /// 很多初始化都可以写在这里 /// </summary> static SingletonSecond() { _SingletonSecond = new SingletonSecond(); } public static SingletonSecond CreateInstance() { return _SingletonSecond; } public static void DoNothing() { } public void Show() { Console.WriteLine($"这里是{this.GetType().Name}.Show"); } public void Log(string text) { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace SingletonPattern { /// <summary> /// 饿汉式 /// </summary> public sealed class SingletonThird { /// <summary> /// 对象会持有资源 /// </summary> private List<string> ConnList = new List<string>() { "这里是数据库连接1", "这里是数据库连接2", "这里是数据库连接3", "这里是数据库连接4", "这里是数据库连接5"//一些其他资源 }; /// <summary> /// 构造函数耗时耗资源 /// </summary> private SingletonThird() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(1000); Console.WriteLine($"{this.GetType().Name}被构造一次"); } /// <summary> /// 静态字段:由CLR保障,在第一次使用这个类型之前,会自动初始化且只初始化一次 /// </summary> private static SingletonThird _SingletonThird = new SingletonThird(); public static SingletonThird CreateInstance() { return _SingletonThird; } public void Show() { Console.WriteLine($"这里是{this.GetType().Name}.Show"); } public void Log(string text) { } } }
最后,想了解更多,可加入CCNetCore社区,橙子带你走上.netcore学习之路。
你可以免费获取到:
站点网址:ccnetcore.com