取到list裡面某個特定的物件 預處理出來一個簡單對映
目錄
一、定義:
保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
通常我們可以讓一個全域性變數使得一個物件被訪問,但它不能防止你例項化多個物件。一個最好的辦法就是,讓類自身負責儲存他的唯一例項。這個類可以保證沒有其他例項可以被建立,並且它可以提供一個訪問該例項的方法。
二、特點:
1.保證唯一例項物件;
2.該單例物件必須由單例類自行建立;
3.單例類對外提供一個訪問該單例的全域性訪問點;
三、優點:
1.保證唯一的例項化
2.單例模式因為Singleton類封裝它的唯一例項,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一例項的受控訪問
四、缺點:
1.單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則;
2.單例模式一般沒有介面,擴充套件困難。如果要擴充套件,則除了修改原來的程式碼,沒有第二種途徑,違背開閉原則;
3.單例模式的功能程式碼通常寫在一個類中,如果功能設計不合理,則很容易違背單一職責原則
五、實現:
①、單例模式
//Single類,定義一個GetInstance操作,允許客戶訪問它的唯一例項。GetInstance是一個靜態方法,主要負責建立自己的唯一例項 class LazySingleton { //懶漢單例:在第一次被引用時,才會將自己例項化。 private static LazySingleton instance; //私有靜態的類變數 private LazySingleton() { } //私有的構造方法,防止外界能夠new例項化它 public static LazySingleton GetInstance() //獲得例項靜態方法 { if (instance == null) //判斷是否有例項 { instance = new LazySingleton(); //建立例項 } return instance; //返回例項 } } //客戶端程式碼 class Program { static void Main(string[] args) { Singleton s1 = Singleton.GetInstance(); Singleton s2 = Singleton.GetInstance(); if (s1 == s2) //比較兩次例項化後物件的結果是否相同 { Console.WriteLine("兩個物件是相同的例項"); } Console.Read(); } }
這樣的話就滿足了單例的效果,保證只例項化一個類,客戶端通過那唯一可以訪問的GetInstance方法來訪問那一個例項。但如果是多個執行緒同時呼叫GetInstance方法,同時執行到了GetInstance方法這兒,它們都會去判斷有沒有被例項化,判斷都為True,那樣的話就建立了兩個例項,就違背了單例模式,不是一個單例。如何解決這樣一個問題呢?
②、多執行緒單例
//單例類 class MultiThreadSingleton { private static MultiThreadSingleton instance; //宣告靜態類變數 private static readonly object syncRoot = new object(); //程式執行時建立一個靜態只讀的程序輔助物件 private MultiThreadSingleton() { } //私有的構造方法,防止外界new例項化它 public static MultiThreadSingleton GetInstance() //建立例項方法 { lock (syncRoot) //加鎖,在同一個時刻加了鎖的那部分程式只有一個執行緒可以進入 { if (instance == null) //判斷是否建立例項 { instance = new MultiThreadSingleton(); //建立例項 } } return instance; //返回例項 } } //客戶端程式碼 class Program { static void Main(string[] args) { MultiThreadSingleton s1 = MultiThreadSingleton.GetInstance(); //呼叫單例類的GetInstance方法,獲得例項 MultiThreadSingleton s2 = MultiThreadSingleton.GetInstance(); //呼叫單例類的GetInstance方法,獲得例項 if (s1 == s2) //判斷兩個例項是否相同 { Console.WriteLine("兩個物件是相同的例項"); //控制檯輸出結果 } Console.Read(); //讀取下一個字元 } }
我們可以讓最先進入的那個執行緒先上一把鎖,建立例項。後面在進入的執行緒就不會再去建立物件例項了,因為第一名來的執行緒已經建立了,你這個判斷的結果是False,自然無法建立了。這樣的話就保證了多個執行緒同時訪問的話不會有多個例項化。解決了上面單例項帶來的問題。但每次進入的執行緒都需要先加鎖在判斷,我都還不知道有沒有建立過這個例項呢你就讓我加鎖,第一名已經例項化過了,我進去再加鎖,在判斷一次,如果有上百個執行緒同時訪問呢,這樣的工作重複上百次,不是很影響我這個程式的效能嗎?我們就可以用到雙重鎖定來解決這個問題
③、雙重鎖定
class MultiThreadSingleton
{
private static MultiThreadSingleton instance; //宣告靜態類變數
private static readonly object syncRoot = new object(); //程式執行時建立一個靜態只讀的程序輔助物件
private MultiThreadSingleton() { } //私有的構造方法,防止外界new例項化它
public static MultiThreadSingleton GetInstance() //此方法是獲得本類例項的唯一全域性訪問點
{
if(instance ==null) //一重:判斷是否建立例項
{
lock (syncRoot) //加鎖,在同一個時刻加了鎖的那部分程式只有一個執行緒可以進入
{
if (instance == null) //二重:判斷若例項不存在,則new一個新例項,否則返回已有的例項
{
instance = new MultiThreadSingleton(); //建立例項
}
}
}
return instance; //返回例項
}
}
//客戶端程式碼
class Program
{
static void Main(string[] args)
{
MultiThreadSingleton s1 = MultiThreadSingleton.GetInstance(); //呼叫單例類的GetInstance方法,獲得例項
MultiThreadSingleton s2 = MultiThreadSingleton.GetInstance(); //呼叫單例類的GetInstance方法,獲得例項
if (s1 == s2) //判斷兩個例項是否相同
{
Console.WriteLine("兩個物件是相同的例項"); //控制檯輸出結果
}
Console.Read(); //讀取下一個字元
}
}
通過這樣兩重的判斷,進入的執行緒不用每次都加鎖,只是在例項未被建立的時候在加鎖處理。同時也保證多執行緒的安全。
④、靜態初始化
C#與公共語言執行庫也提供了一種‘靜態初始化’方法,這種方法不需要開發人員顯示地編寫執行緒安全程式碼,即可解決多執行緒環境下它是不是安全問題。也可以理解為餓漢單例。這種靜態初始化的方式是在自己被載入時將自己例項化,有種迫不及待的感覺
public sealed class EagerSingleton //組織發生派生,而派生可能會增加例項(sealed:密封的)
{
//在第一次引用類的任何成員時建立例項。公共語言執行庫負責處理變數初始化
private static readonly EagerSingleton instance = new EagerSingleton(); //餓漢單例:類一載入就例項化物件,提前佔用系統資源
private EagerSingleton() { }
public static EagerSingleton GetInstance()
{
return instance;
}
}
六、餓漢單例和懶漢單例區別:
例項化 | 執行緒安全 | |
懶漢單例 | 被引用時 | 不安全(通過雙重鎖定保障) |
餓漢單例 | 類一載入時 | 安全 |