//单例模式:就是一个能保证在整个进程中只有一个实例的类
单例模式的基本实现
一.想实现单例模式首先就不能把构造函数公开(私有化构造函数)。
//1.私有化构造函数
private Singleton()
{
}
二.那如何实例化呢?提供一个公开的静态方法。
//2.公开的静态方法提供实例
public static Singleton CreateInstance()
{
return new Singleton();
}
三.通过第二步我们会发现这样写每次还是会产生一个新的实例。如何解决呢?
//3.提供一个静态变量保证重用
private static Singleton _Singleton = null;
这个时候我们就可以改造一下静态方法了
public static Singleton CreateInstance()
{
if (_Singleton == null)
{
_Singleton = new Singleton();
}
return _Singleton;
}
如下就可以基本实现单例模式了
单例模式的线程安全问题
写成这样就完了吗?不可能 新的问题就来了
在静态方法中这个判断只能存在于单线程中 如果有多个线程同时访问这个方法 就会失效
很明显 这个实例被构造了三次,这就已经错了
解决方法一
如何解决这个问题呢,对于多线程并发的问题首先想到的就是最暴力的方法,加锁
双判断锁(懒汉式写法)
什么叫双判断锁呢?我们接着往下看
多线程加锁这个问题大家应该都是很熟悉的
首先定义
private static readonly object Singleton_Look = new object();
接着我们可以改造方法
public static Singleton CreateInstance()
{
lock (Singleton_Look)
{
if (_Singleton == null)
{
_Singleton = new Singleton();
}
return _Singleton;
}
}
这样就能绝对的保证不会有并发的问题,都是一个个的进来的,所以也不会有多个实例
这样就完成了吗?并不是,多线程加锁了那和单线程有什么区别呢!所以我们还需要对这个方法进行改造
public static Singleton CreateInstance()
{
if (_Singleton == null)
{
lock (Singleton_Look)
{
if (_Singleton == null)
{
_Singleton = new Singleton();
}
}
}
return _Singleton;
}
看完这个代码,疑问就来了,为什么要写重复的判断呢,我们看下面这段代码
lock (Singleton_Look)
{
if (_Singleton == null)
{
_Singleton = new Singleton();
}
}
当有线程执行到_Singleton = new Singleton()的时候,后面所有排队的线程进入后都会直接判断_Singleton != null 然后执行返回,这样的逻辑是很浪费的
所以我们在最外层再包一层判断,能保证只要有线程实例化对象以后,后面所有线程不需要走到限制锁这里,会直接返回掉
完整代码如下:
为啥这个称为懒汉式的呢
我们在这个类中写一个这样的静态方法进行调用
public static void Test()
{
Console.WriteLine("Test");
}
我们会发现,调用方法的时候,这个Singleton实例其实还是空的
所以懒汉式写法在你不主动去调用CreateInstance方法的时候,都不会进行实例化
解决方案二
静态构造函数(饿汉式)
使用底层CLR的机制来实现单例模式
代码如图:
为什么这种方式称之为饿汉式呢,在我们调试的时候就会看到,在调用Test方法的时候,首先静态字段会被调用,然后就会执行到静态构造方法
再才会执行到Test方法,
同样我们用多线程进行测试
没有问题
通过调试,细心的小伙伴这个时候就可以发现,静态字段也会和静态构造函数一样,在Test前执行,所以我们的代码可以进行简化
我们可以直接连静态构造函数都不用了,直接通过静态字段来初始化
我们来测试一下
同样也没有问题,这都是基于CLR底层的机制来实现的
********************单例和单线程没有关系!!!!!***********************************
*此处注意一个问题,可能工作中调用实例内中某一个方法,只要执行到这里就报错,但是又找不到问题所在,就可以考虑下是不是静态构造函数内写的东西报错了
其实单例并不是一个好东西,他是有代价的,用了单例,那这个实例就是常驻内存的不会被回收
可以用到单例的地方:线程池,数据库连接池,配置文件对象,IOC容器实例等会需要使用到单例;
原型模式
我们学完单例模式,有一个跟单例模式相似的模式叫原型模式,代码特别的简单,一看就能会
话不多说我们先看代码
小伙伴们是不是很疑惑,这个跟单例模式有什么区别
其实这个是通过MemberwiseClone()来进行内存复制操作,每次都是一个新的实例
我们都知道,引用类型的变量会在堆里面开辟一块空间,而MemberwiseClone会对这个空间进行内存层面的复制
这样每次都是一个新的实例返回出去
看完解读,又懵了,那我要这个模式有什么用,正常实例化不是一样吗?
这个设计模式后面的每一次返回新实例,都是不会走构造函数的,所以如果你需要这个对象很多次,但是构造函数里又有很多不需要的复杂逻辑,我们就可以通过原型模式来进行创建对象,提高效率
以上就是我最近学习的一些内容,若有错误请多多指点!!!!!