理解設計模式之單例模式和原型模式


//單例模式:就是一個能保證在整個進程中只有一個實例的類

單例模式的基本實現

一.想實現單例模式首先就不能把構造函數公開(私有化構造函數)。

//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