單例模式就是保證在整個應用程序的生命周期中,在任何時刻,被指定的類只有一個實例,並為客戶程序提供一個獲取該實例的全局訪問點。
1.經典的模式
namespace singleClass { class OnlyOneClass { private OnlyOneClass() { } private static OnlyOneClass instance; public static OnlyOneClass getInstance() { if (instance == null) { instance = new OnlyOneClass(); return instance; } return instance; } } }
分析一下:
1)通過私有化構造函數,使該類被調用的時候不能通過new來實現
2)定義一個靜態變量類,它的生命周期和程序的生命周期是一樣的,通過該靜態變量來保存該類的實例
2)通過一個靜態方法來實例化自己,並返還實例化後的結果,因為該方法先檢查全局的實例,再判斷是否再創建,保證只有一個實例
但是,這種方式如果碰到了多線程並發,問題就來了,如A,B兩個線程同時訪問了這個類,第一次檢查時候都是null,會出現兩個同時建立自己實例情況,這樣就違背單實例模式的原則
改進一下後:
2.俗稱懶漢模式
namespace singleClass { class OnlyOneClass { private OnlyOneClass() { } public string thisname; private static OnlyOneClass instance; private static object _lock = new object(); public static OnlyOneClass getInstance() { if (instance == null) { lock (_lock) { if (instance == null) { instance = new OnlyOneClass(); return instance; } } } return instance; } } }
解析:
1)聲明一個object 變量,作為lock對象
2)先判斷instance的變量是否為null,如果不為null也就不用lock了,直接返回實例
3) 如果是null,鎖定對象,繼續判斷是否為null,以防有其他線程在lock前已經新建了實例,lock後可以保證在一個線程內操作
3.餓漢模式
class HungerClass{ private HungerClass() { } private readonly static HungerClass instance=new HungerClass (); public static HungerClass getInstance(){ return instance; } }
可以看出這種模式是在類初始化後就已經實例化了instance,不同於上面的懶漢模式時在調用getInstance()方法後實例化。
這種方式下,線程安全的問題將交給CLR。
4.測試
演示一下,通過聲明兩個OneClass類,只一個對其的thisname賦值,然後輸出這兩個類的thisname,看看另一個會怎樣
class Program { static void Main(string[] args) { Console.WriteLine("Get a instanc from OnlyOneClass!"); try { OnlyOneClass one = OnlyOneClass.getInstance();//one 第一個類的變量 Console.WriteLine(one.ToString()); while(true){ string ins = Console.ReadLine(); if (ins != "") { one.thisname = ins; }//只對one實例的thisname賦值 OnlyOneClass two = OnlyOneClass.getInstance();//two 第二個類的變量 Console.WriteLine(one.thisname +" one");//輸出 one實例的thisname Console.WriteLine(two.thisname +" two"); //輸出 two實例的thisname Thread.Sleep(1000); } } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey(); } } }
可以看到這兩個實例都來自一個實例。