来自:CCNetCore社区,一个关注.Netcore领域的社区团队。
单例模式Singleton
单例模式Singleton 创建型设计模式
为了保证对象的唯一性,还可提高程序的性能。
单例模式有3种实现方式
每次创建时进行判断(需要双重判断)
将对象放入静态构造函数种实例
将对象放入静态字段中
注意:单例模式并不能保证多线程的安全性,一万次并发中可能会错误50次左右
Program.cs
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呢
}
Singleton.cs
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++;
}
}
}
SingletonSecond.cs
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)
{
}
}
}
SingletonThird.cs
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学习之路。
你可以免费获取到:
- 设计模式
- 面试八股文
- 问题答疑
- 闲聊茶馆
- Asp.Netcore图文源码解读
- 第一代软件架构单体应用相关技术
- 第二代软件架构分布式应用相关技术
- 第三代软件架构微服务应用相关技术
- 第四代软件架构网格服务应用相关技术
站点网址:ccnetcore.com