單例模式(Singleton Pattern)

  • 單例模式概述

定義確保一個類只有一個實例,並提供一個全局訪問點來訪問這個實例html

簡單的說,就是你有且只有一個女友(有多個女友的模式不是這裏~~),而且你的女友很特殊,歷來只聽你的話,因此別人想和她交流(訪問她)就必須經過你(全局訪問點)來和她交流。多線程

系統中用到單例模式的地方不少,好比Windows系統點擊開始只能出現一個開始界面,Ctrl+Alt+. 只能出現一個資源管理器,每一個進程有且對應惟一一個進程ID等等。單例模式是爲了讓資源獲得最大化利用,不浪費資源。同時假如不採用此模式,就可能在不一樣時刻打開同一個界面,但界面中的內容又各不相同,於是用戶極易產生誤解,影響使用效率。所以單例模式在系統中的應用很是重要。併發

要點a.某一個單例類只能有一個實例函數

      b.必須自行建立這個實例高併發

      c.必須向系統提供這個實例;性能

  • 單例模式的結構與實現

結構: spa

  1. 咱們考慮一個問題,每個類都會有它的默認構造函數,或者咱們重寫一個構造函數,這個函數都是在建立這個類的實例的時候自動調用的。即對對象進行初始化操做。因此咱們每次new的時候都會有一個新的對象,固然這是不符合單例模式的要求的。所以爲了知足單例模式的要求,就必須對類中的函數進行修改,怎麼修改呢,一步一步來(我的以爲這個理解仍是挺重要的)。
      1. 首先,咱們類的實例化不能在外部進行(單例類自行提供這個實例),即不能每次new都調用構造函數,所以將構造函數設爲private類型
      2. 既然構造函數是private的,那怎麼樣調用呢??經過公有方法能夠調用private函數,返回實例
      3. 看到這裏,或許有人就有疑問了,公有方法(public ... ....)是在類的對象生成後才能夠調用的,可是對象的建立又必須經過構造函數,而這裏構造函數又必須經過公有方法調用,不就造成了一個雞生蛋,蛋孵雞的問題了嗎??好了,誰先誰後,咱們先辯論下吧.....其實,靜態方法的使用,能很好的解決這個問題,將公有方法(函數)設爲靜態方法,咱們就能經過類名.方法名去調用它,繼而調用私有構造函數,產生對象。
      4. 再有,單例模式建立的女友只能是一個,那又怎麼肯定呢?怎麼肯定你的女友是惟一的並且你沒有偷換呢?(~~皮一下),咱們就要爲這個單例類添加一個變量了,用來確認是否惟一,因爲建立的整個入口是靜態公有方法,因此在那時就要判斷是否惟一了,若是該單例類有了一個女友,判斷後便再也不建立,若是當前沒有,則分配一個。而靜態成員函數能夠直接訪問類的靜態數據和函數成員,而訪問非靜態成員,必須經過對象名,這有陷入雞和蛋的問題裏了,因此,該變量設爲靜態變量就很方便了,它也知足爲整個類服務的特性。
      5. 總結下步驟 :(1)私有構造函數;(2)公有方法調用私有函數;(3)設爲靜態方法;(4)添加靜態變量;(5)根據變量數判斷是否生成女友(惟一的對象);
  2. 上圖中:↓↓↓↓↓
  3. Singleton(單例),在單例類的內部建立它的惟一實例
  4. Getinstance(靜態方法),經過它產生惟一實例
  5. instance(靜態變量),判斷是否能夠產生實例

 實現:線程

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Singleton
 7 {
 8     class Singleton
 9 {
10     private static Singleton instance = null;//靜態私有成員變量
11     
12     //私有構造函數
13     private Singleton()
14     {
15         Console.WriteLine("恭喜你,得到一個女友~~");
16     }
17     
18     //靜態公有方法,返回實例
19     public static Singleton Getinstance()
20     {
21         if(instance == null)//沒女友
22             instance = new Singleton();//生成一個吧
23         return instance;//有就返回當前的,
24     }
25 }
26     class Program
27     {
28         static void Main(string[] args)
29         {
30             Singleton s1 = Singleton.Getinstance();
31             Singleton s2 = Singleton.Getinstance();
32             if (s1 == s2)
33             {
34                 Console.WriteLine("怎麼能想要共有一個女友呢?S2 趕忙換一個吧...");
35             }
36         }
37     }
38 }

 結果:設計

  •  餓漢式單例和懶漢式單例

 剛看到這兩個單例的名字時仍是有點可笑的,如此這麼生動形象呢,就好像餓漢式單身(連溫飽都知足不了,何來女友呢),懶漢式(好吃懶作的,也很難...)不亂扯了,回主題。餓漢式單例正如餓漢同樣,很餓很餓的人最想要的就是當即立刻吃東西。所以餓漢式單例在定義靜態變量時就實例化了單例類,由於實在太餓了啊,等不及了code

 1 class EagetSingleton
 2 {
 3     private static EagetSingleton instance = new EagetSingleton();//靜態變量實例化單例類
 4     
 5     private EagetSingleton(){}
 6     
 7     public static EagetSingleton GetInstance()
 8     {
 9         return instance;
10     }
11 }

 

懶漢式單例類則是在類第一次被引用時將本身實例化,單例類被加載時不會實例化,因此這很符合懶漢的氣質~可是在這裏要注意的是,在定義靜態變量時沒有實例化單例類,而是在第一次調用靜態方法時實例化單例類,這就會產生問題,高併發,多線程實現懶漢式單例時會建立多個對象,從而違背了單例模式的設計意圖。也就是仍是要對女友的個數進行判斷。這要怎麼辦呢?在多線程的狀況下,就要對該代碼段進行控制,即每次只讓一個線程進入並建立實例,也就是至關於如今的「共享女朋友」,幫你拍照啊,陪你去看電影啊 ,巴拉巴拉。可是,該「共享女朋友」有且只有一個,即單例類的惟一實例。因此,土豪們(各個線程)得一個一個租用,上一個用完了下一個才能租用。所以代碼以下:

 1 class LazytSingleton
 2 {
 3     private static LazytSingleton instance = null;
 4     private static readonly object synRoot = new object();//看作一個門。
 5     //程序運行時建立只讀輔助對象
 6         
 7     private LazytSingleton(){}
 8     
 9     public static LazytSingleton GetInstance()
10     {
11         if(instance == null)//在房間外問:房間裏有人嗎 ? 沒人迴應 ,可能沒,可能下一秒有人進去 ,我卻覺得沒人
12         {
13             lock(synRoot)//第二次判斷 //把門關了,外面線程進不來,只能裏面的出來,外面的才能進
14             {
15                 if(instance == null)//繼續問,房間裏有人嗎?  有就真的有,沒有就真的沒
16                 {
17                     instance = new LazytSingleton();//建立實例
18                 }
19             }
20         }
21     }
22 }

兩者比較:

  • 餓漢式單例

優勢:無需考慮多線程同時訪問的問題,確保實例惟一性。調用速度和反應時間快於懶漢模式,由於餓漢一開始就建立,後面則直接拿來用就能夠了。

缺點:無論單例對象是否須要,都會在類加載時建立,這樣不如懶漢式單例,資源利用不高,且加載時間較長。如啓動VS,Eclipse等,須要loading許多可能要的可能不要的,要等啊...

  • 懶漢式單例

優勢:第一次使用時建立,不會一直佔用資源,即延遲加載。

缺點:必須考慮多線程問題,特別是單例類做爲資源控制器時,會涉及資源初始化,也會耗費許多時間,也會出現多線程同時首次引用此類,形成擁堵,致使系能性能下降

 

  •  單例模式的優缺點和適用環境

  •  單例模式的優勢
  1. 提供惟一實例的受控訪問,能夠嚴格控制什麼時候訪問
  2. 因爲只存在一個對象,能夠節省系統資源
  3. 若是將單例模式的實例數目變爲可變,即將單例模式進行擴展,便可得到指定數目的實例對象,即節省資源,又提升效率(至關於多例類)
  • 單例模式的缺點
  1. 沒有抽象層,擴展有較大困難
  2. 單例類職責太重,必定程度上違背了單一職責原則(單例類即提供業務方法,又提供了建立對象的方法(工廠方法),對象建立和對象自己耦合在了一塊兒)
  3. C#、JAVA 擁有GC(自動垃圾回收機制) (能夠去了解下) ,在實例化的對象長時間不被利用,會被誤認爲垃圾進行自動銷燬,下次利用又要從新建立,致使共享的單例對象丟失(女友丟了可很差受啊..)
  • 單例模式的適用環境
  1. 在系統只要一個實例對象(man只要一個girlfriend)/(資源管理器).....
  2. 客戶調用類的單個實例只容許使用一個公共訪問點,除了該點外,不容許其餘途徑來訪問實例
相關文章
相關標籤/搜索