小菜學習設計模式(二)—單例(Singleton)模式


前言

設計模式目錄:

本篇目錄:

  單例模式(Singleton)可以說是最簡單的模式,對.net來說,因為不需要考慮到垃圾回收機制,實現起來很簡單,但是對於沒有提供內存管理的平台來說,比如C++,因為單例模式只考慮創建對象,所以使用的時候要考慮全面些。

  其實說到些設計模式,我們有時候用到的真的很少,就像飛機零部件的模具不適用於汽車制造一樣,某些設計模式也只在特定的環境下使用,單例模式的使用場景一般是資源管理器等,像說的最多的就是打印機場景:每台計算機可以有若干個打印機,但只能有一個Printer Spooler,以避免兩個打印作業同時輸出到打印機中。每台計算機可以有若干傳真卡,但是只應該有一個軟件負責管理傳真卡,以避免出現兩份傳真作業同時傳到傳真卡中的情況。每台計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。說白點就是一個男人可以有很多女朋友,但是結婚生子的只能是其中一個。一夫多妻的情況就不是單例模式了,那應該是“多態”了。哈哈。

簡單實現

  單例模式(Singleton)在.net中的定義是:一個類有且僅有一個實例,並且自行實例化向整個系統提供。

  從定義中我們可以看出,單例模式所具有的三個要點:

  • 某個類只能有一個實例
  • 必須自行創建這個實例
  • 必須自行向整個系統提供這個實例

  根據所說的要點,我們可以在.net中這樣簡單的實現:

 1     public class SingletonTest
 2     {
 3         public static SingletonTest model;
 4         private SingletonTest()
 5         { }
 6         public static SingletonTest getSingleton()
 7         {
 8             if (model==null)
 9             {
10                 model = new SingletonTest();
11             }
12             return model;
13         }
14     }

   代碼就這么簡單,在getSingleton()方法返回實例的時候要先判斷對象是否已經被實例化,如果是就不需要重新創建了。

線程安全

  上面的代碼看起來沒什么問題,但是在多線程的情況下就會出現問題,我們來開幾個線程測試下:

 1     public class SingletonTest
 2     {
 3         public static SingletonTest model;
 4         private SingletonTest()
 5         { }
 6         public static SingletonTest getSingleton()
 7         {
 8             if (model==null)
 9             {
10                 Console.WriteLine(String.Format("我是被線程:{0}創建的!", Thread.CurrentThread.Name));
11                 model = new SingletonTest();
12             }
13             return model;
14         }
15     }
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Program p1 = new Program();
 6             p1.Test();
 7             Console.ReadLine();
 8         }
 9 
10         private void Test()
11         {
12             Thread newThread;
13             ThreadStart ts = new ThreadStart(DoWork);
14             for (int counter = 1; counter < 6; counter++)
15             {
16                 newThread = new Thread(ts);
17                 newThread.Name = "蟋蟀" + counter;
18                 newThread.Start();
19             }
20         }
21 
22         protected void DoWork()
23         {
24             //調用返回對象方法
25             SingletonTest.getSingleton();
26         }
27     }

  執行結果:

  根據上圖的執行結果,會發現SingletonTest對象被實例化了2次,按照單例模式(Singleton)的特性:一個類只能有一個實例,那就不是單例模式了,為什么會實例化兩次呢?因為我們的計算機執行速度很快,在某一個時間點,線程1在執行完if (model==null)這段代碼,還沒執行model = new SingletonTest(),線程2剛好執行判斷對象是否null,就是說線程1和線程2都會進入下面的if判斷體中實例化對象。

  關於單例模式的線程安全問題,網上一找一大堆,在《漫談設計模式》這本書中,作者也提到了線程安全問題,java中是使用的是“Double-Check Locking”方法,還有序列化的問題,這邊先不考慮,其實在.net中解決線程安全的問題也很簡單,就是用lock鎖,我們根據上面的代碼,再來修改下,然后做個測試:

 1     public class SingletonTest
 2     {
 3         private static SingletonTest singleton;
 4         private static readonly object syncObject = new object();
 5         /// <summary>
 6         /// 構造函數必須是私有的
 7         /// 這樣在外部便無法使用 new 來創建該類的實例
 8         /// </summary>
 9         private SingletonTest()
10         { }
11         /// <summary>
12         /// 定義一個全局訪問點
13         /// 設置為靜態方法
14         /// 則在類的外部便無需實例化就可以調用該方法
15         /// </summary>
16         /// <returns></returns>
17         public static SingletonTest getSingleton()
18         {
19             //這里可以保證只實例化一次
20             //即在第一次調用時實例化
21             //以后調用便不會再實例化
22             //第一重 singleton == null
23             if (singleton == null)
24             {
25                 lock (syncObject)
26                 {
27                     //第二重 singleton == null
28                     if (singleton == null)
29                     {
30                         Console.WriteLine(String.Format("我是被線程:{0}創建的!", Thread.CurrentThread.Name));
31                         singleton = new SingletonTest();
32                     }
33                 }
34             }
35             return singleton;
36         }
37     }

   執行結果:

  從上面的執行結果我們就可以看到,對象僅被實例化了一次,在某段代碼體中,只能有且只有一個線程訪問,加鎖的目的就好比:我們去火車站售票大廳買票,因為買票的人太多,為了緩解壓力,就多開了幾個售票窗口(線程),比如南京到徐州的G110次列車只有一張票,窗口A和窗口B的人同時都在買這一班次的票,這時候就要加鎖,不然就有可能會出現只有一張票,但是賣出去兩張。話題跑偏了,哈哈。

  示例代碼下載:Singleton.rar

后記

  關於模式,再多說兩句,在某些情況下,像上面所說的:模式可以看成現實生活中的模具,有些產品(項目)是由一種模具(模式)生成出來的,比如杯子、瓶子等,有些產品(項目)是由多種模具(模式)生成出來,然后組合而成的,比如汽車、飛機等,是由成千上萬個零部件組合形成的。就是說學會模式后要會懂得組合,而且要“合適”的組合,這樣才會做出一個完善的產品(項目)。

  還是那就話:騷年們,和小菜一起整理學習吧,未完待續。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM