單件模式(Singleton Pattern)
——.NET設計模式系列之二html
Terrylee,2005年12月07日設計模式
概述安全
Singleton模式要求一個類有且僅有一個實例,而且提供了一個全局的訪問點。這就提出了一個問題:如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?客戶程序在調用某一個類時,它是不會考慮這個類是否只能有一個實例等問題的,因此,這應該是類設計者的責任,而不是類使用者的責任。服務器
從另外一個角度來講,Singleton模式其實也是一種職責型模式。由於咱們建立了一個對象,這個對象扮演了獨一無二的角色,在這個單獨的對象實例中,它集中了它所屬類的全部權力,同時它也肩負了行使這種權力的職責!多線程
意圖併發
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。ide
模型圖函數
邏輯模型圖:post
物理模型圖:性能
生活中的例子
美國總統的職位是Singleton,美國憲法規定了總統的選舉,任期以及繼任的順序。這樣,在任什麼時候刻只能由一個現任的總統。不管現任總統的身份爲什麼,其頭銜"美利堅合衆國總統"是訪問這個職位的人的一個全局的訪問點。
五種實現
1.簡單實現
2 {
3 static Singleton instance=null;
4
5 Singleton()
6 {
7 }
8
9 public static Singleton Instance
10 {
11 get
12 {
13 if (instance==null)
14 {
15 instance = new Singleton();
16 }
17 return instance;
18 }
19 }
20}
這種方式的實現對於線程來講並非安全的,由於在多線程的環境下有可能獲得Singleton類的多個實例。若是同時有兩個線程去判斷(instance == null),而且獲得的結果爲真,這時兩個線程都會建立類Singleton的實例,這樣就違背了Singleton模式的原則。實際上在上述代碼中,有可能在計算出表達式的值以前,對象實例已經被建立,可是內存模型並不能保證對象實例在第二個線程建立以前被發現。
該實現方式主要有兩個優勢:
l 因爲實例是在 Instance屬性方法內部建立的,所以類可使用附加功能(例如,對子類進行實例化),即便它可能引入不想要的依賴性。
l 直到對象要求產生一個實例才執行實例化;這種方法稱爲「惰性實例化」。惰性實例化避免了在應用程序啓動時實例化沒必要要的 singleton。
2.安全的線程
2 {
3 static Singleton instance=null;
4 static readonly object padlock = new object();
5
6 Singleton()
7 {
8 }
9
10 public static Singleton Instance
11 {
12 get
13 {
14 lock (padlock)
15 {
16 if (instance==null)
17 {
18 instance = new Singleton();
19 }
20 return instance;
21 }
22 }
23 }
24}
25
26
這種方式的實現對於線程來講是安全的。咱們首先建立了一個進程輔助對象,線程在進入時先對輔助對象加鎖而後再檢測對象是否被建立,這樣能夠確保只有一個實例被建立,由於在同一個時刻加了鎖的那部分程序只有一個線程能夠進入。這種狀況下,對象實例由最早進入的那個線程建立,後來的線程在進入時(instence == null)爲假,不會再去建立對象實例了。可是這種實現方式增長了額外的開銷,損失了性能。
3.雙重鎖定
2 {
3 static Singleton instance=null;
4 static readonly object padlock = new object();
5
6 Singleton()
7 {
8 }
9
10 public static Singleton Instance
11 {
12 get
13 {
14 if (instance==null)
15 {
16 lock (padlock)
17 {
18 if (instance==null)
19 {
20 instance = new Singleton();
21 }
22 }
23 }
24 return instance;
25 }
26 }
27}
28
這種實現方式對多線程來講是安全的,同時線程不是每次都加鎖,只有判斷對象實例沒有被建立時它才加鎖,有了咱們上面第一部分的裏面的分析,咱們知道,加鎖後還得再進行對象是否已被建立的判斷。它解決了線程併發問題,同時避免在每一個 Instance屬性方法的調用中都出現獨佔鎖定。它還容許您將實例化延遲到第一次訪問對象時發生。實際上,應用程序不多須要這種類型的實現。大多數狀況下咱們會用靜態初始化。這種方式仍然有不少缺點:沒法實現延遲初始化。
4.靜態初始化
2 {
3 static readonly Singleton instance=new Singleton();
4
5 static Singleton()
6 {
7 }
8
9 Singleton()
10 {
11 }
12
13 public static Singleton Instance
14 {
15 get
16 {
17 return instance;
18 }
19 }
20}
21
看到上面這段富有戲劇性的代碼,咱們可能會產生懷疑,這仍是Singleton模式嗎?在此實現中,將在第一次引用類的任何成員時建立實例。公共語言運行庫負責處理變量初始化。該類標記爲 sealed 以阻止發生派生,而派生可能會增長實例。此外,變量標記爲 readonly,這意味着只能在靜態初始化期間(此處顯示的示例)或在類構造函數中分配變量。
該實現與前面的示例相似,不一樣之處在於它依賴公共語言運行庫來初始化變量。它仍然能夠用來解決 Singleton模式試圖解決的兩個基本問題:全局訪問和實例化控制。公共靜態屬性爲訪問實例提供了一個全局訪問點。此外,因爲構造函數是私有的,所以不能在類自己之外實例化 Singleton 類;所以,變量引用的是能夠在系統中存在的惟一的實例。
因爲 Singleton 實例被私有靜態成員變量引用,所以在類首次被對 Instance屬性的調用所引用以前,不會發生實例化。
這種方法惟一的潛在缺點是,您對實例化機制的控制權較少。在 Design Patterns形式中,您可以在實例化以前使用非默認的構造函數或執行其餘任務。因爲在此解決方案中由 .NET Framework 負責執行初始化,所以您沒有這些選項。在大多數狀況下,靜態初始化是在 .NET 中實現 Singleton的首選方法。
5.延遲初始化
2 {
3 Singleton()
4 {
5 }
6
7 public static Singleton Instance
8 {
9 get
10 {
11 return Nested.instance;
12 }
13 }
14
15 class Nested
16 {
17 static Nested()
18 {
19 }
20
21 internal static readonly Singleton instance = new Singleton();
22 }
23}
24
這裏,初始化工做有Nested類的一個靜態成員來完成,這樣就實現了延遲初始化,並具備不少的優點,是值得推薦的一種實
現方式。
本 人 添 加
通常項目中都這樣作
public class Student { public static readonly Student Instance = new Student(); private Student() { } //... }
這個是線程安全的, 而且也是最簡單的寫法.
實現要點
l Singleton模式是限制而不是改進類的建立。
l Singleton類中的實例構造器能夠設置爲Protected以容許子類派生。
l Singleton模式通常不要支持Icloneable接口,由於這可能致使多個對象實例,與Singleton模式的初衷違背。
l Singleton模式通常不要支持序列化,這也有可能致使多個對象實例,這也與Singleton模式的初衷違背。
l Singleton只考慮了對象建立的管理,沒有考慮到銷燬的管理,就支持垃圾回收的平臺和對象的開銷來說,咱們通常不必對其銷燬進行特殊的管理。
l 理解和擴展Singleton模式的核心是「如何控制用戶使用new對一個類的構造器的任意調用」。
l 能夠很簡單的修改一個Singleton,使它有少數幾個實例,這樣作是容許的並且是有意義的。
優勢
l 實例控制:Singleton會阻止其餘對象實例化其本身的 Singleton對象的副本,從而確保全部對象都訪問惟一實例
l 靈活性:由於類控制了實例化過程,因此類能夠更加靈活修改實例化過程
缺點
l 開銷:雖然數量不多,但若是每次對象請求引用時都要檢查是否存在類的實例,將仍然須要一些開銷。能夠經過使用靜態初始化解決此問題,上面的五種實現方式中已經說過了。
l 可能的開發混淆:使用 singleton對象(尤爲在類庫中定義的對象)時,開發人員必須記住本身不能使用 new 關鍵字實例化對象。由於可能沒法訪問庫源代碼,所以應用程序開發人員可能會意外發現本身沒法直接實例化此類。
l 對象的生存期:Singleton 不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基於 .NET Framework 的語言),只有 Singleton類可以致使實例被取消分配,由於它包含對該實例的私有引用。在某些語言中(如 C++),其餘類能夠刪除
對象實例,但這樣會致使 Singleton類中出現懸浮引用。
適用性
l 當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。
l 當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。
應用場景
l 每臺計算機能夠有若干個打印機,但只能有一個Printer Spooler,避免兩個打印做業同時輸出到打印機。
(摘自呂震宇的C#設計模式(7)-Singleton Pattern)
l PC機中可能有幾個串口,但只能有一個COM1口的實例。
l 系統中只能有一個窗口管理器。
l .NET Remoting中服務器激活對象中的Sigleton對象,確保全部的客戶程序的請求都只有一個實例來處理。
完整示例
這是一個簡單的計數器例子,四個線程同時進行計數。
2 using System.Threading;
3
4 namespace SigletonPattern.SigletonCounter
5 {
6 /// <summary>
7 /// 功能:簡單計數器的單件模式
8 /// 編寫:Terrylee
9 /// 日期:2005年12月06日
10 /// </summary>
11 public class CountSigleton
12 {
13 ///存儲惟一的實例
14 static CountSigleton uniCounter = new CountSigleton();
15
16 ///存儲計數值
17 private int totNum = 0;
18
19 private CountSigleton()
20
21 {
22 ///線程延遲2000毫秒
23 Thread.Sleep(2000);
24 }
25
26 static public CountSigleton Instance()
27
28 {
29
30 return uniCounter;
31
32 }
33
34 ///計數加1
35 public void Add()
36 {
37 totNum ++;
38 }
39
40 ///得到當前計數值
41 public int GetCounter()
42 {
43 return totNum;
44 }
45
46 }
47}
48
2 using System.Threading;
3 using System.Text;
4
5 namespace SigletonPattern.SigletonCounter
6 {
7 /// <summary>
8 /// 功能:建立一個多線程計數的類
9 /// 編寫:Terrylee
10 /// 日期:2005年12月06日
11 /// </summary>
12 public class CountMutilThread
13 {
14 public CountMutilThread()
15 {
16
17 }
18
19 /// <summary>
20 /// 線程工做
21 /// </summary>
22 public static void DoSomeWork()
23 {
24 ///構造顯示字符串
25 string results = "";
26
27 ///建立一個Sigleton實例
28 CountSigleton MyCounter = CountSigleton.Instance();
29
30 ///循環調用四次
31 for(int i=1;i<5;i++)
32 {
33 ///開始計數
34 MyCounter.Add();
35
36 results +="線程";
37 results += Thread.CurrentThread.Name.ToString() + "——〉";
38 results += "當前的計數:";
39 results += MyCounter.GetCounter().ToString();
40 results += "\n";
41
42 Console.WriteLine(results);
43
44 ///清空顯示字符串
45 results = "";
46 }
47 }
48
49 public void StartMain()
50 {
51
52 Thread thread0 = Thread.CurrentThread;
53
54 thread0.Name = "Thread 0";
55
56 Thread thread1 =new Thread(new ThreadStart(DoSomeWork));
57
58 thread1.Name = "Thread 1";
59
60 Thread thread2 =new Thread(new ThreadStart(DoSomeWork));
61
62 thread2.Name = "Thread 2";
63
64 Thread thread3 =new Thread(new ThreadStart(DoSomeWork));
65
66 thread3.Name = "Thread 3";
67
68 thread1.Start();
69
70 thread2.Start();
71
72 thread3.Start();
73
74 ///線程0也只執行和其餘線程相同的工做
75 DoSomeWork();
76 }
77 }
78}
79
2 using System.Text;
3 using System.Threading;
4
5 namespace SigletonPattern.SigletonCounter
6 {
7 /// <summary>
8 /// 功能:實現多線程計數器的客戶端
9 /// 編寫:Terrylee
10 /// 日期:2005年12月06日
11 /// </summary>
12 public class CountClient
13 {
14 public static void Main(string[] args)
15 {
16 CountMutilThread cmt = new CountMutilThread();
17
18 cmt.StartMain();
19
20 Console.ReadLine();
21 }
22 }
23}
24
總結
Singleton設計模式是一個很是有用的機制,可用於在面向對象的應用程序中提供單個訪問點。文中經過五種實現方式的比較和一個完整的示例,完成了對Singleton模式的一個總結和探索。用一句廣告詞來歸納Singleton模式就是「簡約而不簡單」。
原地址:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html