//單例模式:就是一個能保證在整個進程中只有一個實例的類
單例模式的基本實現
一.想實現單例模式首先就不能把構造函數公開(私有化構造函數)。
//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會對這個空間進行內存層面的復制
這樣每次都是一個新的實例返回出去
看完解讀,又懵了,那我要這個模式有什么用,正常實例化不是一樣嗎?
這個設計模式后面的每一次返回新實例,都是不會走構造函數的,所以如果你需要這個對象很多次,但是構造函數里又有很多不需要的復雜邏輯,我們就可以通過原型模式來進行創建對象,提高效率
以上就是我最近學習的一些內容,若有錯誤請多多指點!!!!!
