理解设计模式之单例模式和原型模式


//单例模式:就是一个能保证在整个进程中只有一个实例的类

单例模式的基本实现

一.想实现单例模式首先就不能把构造函数公开(私有化构造函数)。

//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会对这个空间进行内存层面的复制

这样每次都是一个新的实例返回出去

看完解读,又懵了,那我要这个模式有什么用,正常实例化不是一样吗?

这个设计模式后面的每一次返回新实例,都是不会走构造函数的,所以如果你需要这个对象很多次,但是构造函数里又有很多不需要的复杂逻辑,我们就可以通过原型模式来进行创建对象,提高效率 

 

以上就是我最近学习的一些内容,若有错误请多多指点!!!!!

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM