目录
一、概述
1)基础
二、实现
1)单线程
2)解决——多线程情况下
解决方案一:Sleep
解决方案二:加锁
三、扩展
一、概述
单例模式->创建型设计模式
定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。(在第一个使用者创建了这个类的实例之后,其后需要使用这个类就只能使用之前创建的实例,无法再创建一个新的实例。)
1)基础
“确保一个类只有一个实例” + “提供一个访问它的全局访问点”
首先,我们再创建类的实例会想到用什么方式来创建?new。
那么为什么可以通过它来创建一个实例?因为构造函数,若未在类中定义构造函数,则编译器会自动帮我们生成一个无参的构造函数。如果类定义私有的构造函数就不能在外界通过new创建实例了。【故,确保一个类只有一个实例,利用“定义私有构造函数”+“定义一个保存该实例的静态私有变量”(为什么定义为静态?静态是为了在多线程中确保只有一个实例)】
其次,关于提供实例的全局访问点?可以定义一个公有方法或者属性来把该类的实例公开出去。
二、实现
1)单线程
通过索引器实现:目标实例只可存在一个
- 实现原理:get中判断目标实例是否为空,为空则new一个,反则不进行操作,返回一个存在的实例。
-
索引器小知识:get 获取属性值, set 设置属性值。
-
public class Prpgram
{
static void Main(String[] args)
{
PrA a = PrA.Intance; //对象1
PrA b = PrA.Intance; //对象2
Console.Read();
}
///实例类
public class PrA
{
//定义一个PrA类型的静态变量来保存类的实例
private static PrA intance = null;
//定义公有属性来提供全局访问点
public static PrA Intance
{
get
{
if (intance is null) //决定是否实例化对象,管理实例化的操作
{
intance = new PrA();
}
return intance;
}
}
//定义私有构造函数,使外界不能创建该类实例
private PrA()
{
Console.WriteLine("实例化!");
}
}
}
-
结论:单例模式下,任何地方对单例类创建对象,程序最多只存在一个实例。
(字段命名规范,首字母大写的是给外部使用,小写的给内部使用。)
-
注意:以上只适合单线程情况
- 多线程情况下产生问题
- 结果:多次实例化
-
解释:
- 打印多个?因为同一个时间片完成n个线程的输出,即同时完成对intance字段的get获取,皆为null时访问,故将访问n次实例化n个对象。
- for循环20次却只产生小于20个的对象?线程创建需要时间,而Mian方法中代码执行速度很快,即有可能执行至程序结束时部分线程还未创建完成。
2)解决——多线程情况下
解决方案一:Sleep
- 实现:在for中利用Sleep解决,达到线程非同时进行。
- 存在问题:多线程之间切记不使用Sleep直接停止线程。(这里不详细介绍)
解决方案二:加锁
利用双层加锁
...
// 双重锁定只需要一句判断就可以了
if (intance is null)
{
lock (objlock) //加锁
{
if (intance is null) //实例是否存在
{
intance = new PrA();
}
return intance;
}
}
...
三、扩展
先看以下代码
public class Prpgram
{
static void Main(String[] args)
{
List<PrA> list = new List<PrA>();
for (int i = 0; i < 20; i++)
{
Thread th = new Thread(x =>
{
PrA pa = PrA.Intance; //创建了很多对象
list.Add(pa);
});
th.IsBackground = true;
th.Start();
}
Thread.Sleep(500); //防止执行至程序结束时线程还未创建完成
list[0].list_ss.Add(new ss() { name = "aa", age = 123 }); //给第一添加数据
list[0].id = 1;
list[6].id = 2;
foreach (var item in list)
{
if (item.list_ss.Count > 0)
{
Console.WriteLine($"{item.list_ss[0].name}-{item.list_ss[0].age}-{item.id}");
}
}
Console.Read();
}
///实例类
public class PrA
{
//静态PrA类型的字段
private static PrA intance = null;
private static object objlock = new object();
public static PrA Intance
{
get
{
lock (objlock)
{
if (intance is null) //决定是否实例化对象,管理实例化的操作
{ intance = new PrA(); }
return intance;
}
}
}
//构造函数
private PrA()
{ Console.WriteLine("实例化!"); }
public List<ss> list_ss = new();
public int id = 0;
}
public class ss
{
public string name { get; set; }
public int age { get; set; }
}
}
借鉴大佬文章:https://www.cnblogs.com/zhili/p/SingletonPatterm.html