請不要濫用設計模式——SingleTon篇

來源:互聯網
上載者:User

前言

    說到物件導向的設計模式,現在很多人都可以隨便說出好幾種常用的,但是有沒有想過設計模式,即使是初學者也至少能說一下SingleTon和Factory Method這兩個。

    那麼,設計模式是不是隨便怎麼用都沒問題哪?

    這個問題從提問的方式上就可以看出,答案一定是否定的(大家也不是白白接受了這麼多年的應試教育的)。

    但是,就我個人的觀察,濫用設計模式的絕對不是少數。而且越是簡單的模式越會被濫用。

從最簡單的模式——SingleTon開始

    說到SingleTon,我相信只要知道設計模式的,就知道SingleTon,也寫過SingleTon,可謂是盡人皆知的設計模式了。

    就是這個盡人皆知的設計模式,卻是被濫用的最厲害的設計模式,本篇就討論一下關於SingleTon的濫用問題。

安全執行緒是個問題

    首先GoF是站在一個純OO的領域思考問題的,所以,很多其他領域的問題並沒有考慮進來(事實上也不適合拉進來一起講),然而實際編程者卻不得不面對更多領域的問題(最常見的是並發領域)。

    這也就是為什麼在GoF的SingleTon是如此的簡單,而在Java或.net實際寫SingleTon時,卻需要注意鎖的問題的根本原因。

    關於SingleTon是否是安全執行緒的,我傾向於把問題分解成兩個部分:

  • SingleTon本身是否安全執行緒
  • SingleTon的執行個體是否安全執行緒

    關於第一個,通過著名的Java下雙檢鎖不安全問題,相信大家都已經十分清楚了,如果還有不清楚的,請查閱相關資料,本文就不再重複了。

    在這裡,要重點說的是第二個問題,SingleTon的執行個體是否安全執行緒。

    如果仔細看msdn的話,在大多數類上,msdn都寫了這麼一句話:
Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

    在msdn中文版中是:

此類型的公用靜態(在 Visual Basic 中為 Shared)成員是安全執行緒的。但不能保證任何執行個體成員是安全執行緒的。

    在ms給出的類庫,把靜態成員都寫成安全執行緒的,而對待大部分的執行個體成員卻是放任其線程的不安全,而要求開發人員在開發時處理執行個體成員的線程不安全問題(我相信Java的類庫也是類似的做法)。

    那麼在多線程程式中使用SingleTon,會引出什麼問題哪?

    首先,SingleTon僅僅允許一個類只有一個執行個體。那麼這裡簡單的做個推理:

  1. 在這個程式裡面無論何時,當你需要這個類的執行個體的時候,都只能使用這個唯一的執行個體
  2. 無論在這個程式的哪個線程中,當你需要這個類的執行個體的時候,都只能使用這個唯一的執行個體
  3. 無論在這個程式中出現什麼樣的並發,當你需要這個類的執行個體的時候,都只能使用這個唯一的執行個體
  4. 無論在這個程式中出現什麼樣的並發,當你需要調用這個類的執行個體的某個成員時,都只能使用這個唯一的執行個體的成員
  5. 無論在這個程式中出現什麼樣的並發,為了保證程式是安全執行緒的,當你需要調用這個類的執行個體的某個成員時,都只能使用這個唯一的執行個體的成員,並且需要保證其安全執行緒
  6. 無論在這個程式中出現什麼樣的並發,為了保證程式是安全執行緒的,當你需要調用這個類的執行個體的某個成員時,如果這個成員不是安全執行緒的,那麼在使用這個唯一的執行個體的成員時都需要正確的線程同步

    發現問題了沒有,被SingleTon的執行個體成員的執行緒安全性需要誰來保證?

  • 選項A:SingleTon的執行個體保證所有的成員是安全執行緒的
  • 選項B:SingleTon的執行個體不保證安全執行緒的,請大家在使用的時候都加上鎖

    如果選擇A,那麼,請檢查那些被SingleTon的代碼,看看有沒有用到堆,檢查對堆的任何調用是否都是安全執行緒的,或者已經同步的

    如果選擇B,那麼,請檢查所有使用SingleTon的代碼,看看是否都經過了可靠的線程同步(例如Lock那個SingleTon的對象),只要有一處不注意,就導致整個是線程不安全的(但是當下這麼多寫網頁的人,有多少人會去關注那些資源是需要線程同步的嗎?)。

    有沒有選項C?有,如果是類庫的話,對外宣稱程式是線程不安全的,請在使用前保證安全執行緒(例如COM中著名的STA);如果是應用程式的話就告訴客戶,本程式是線程不安全的,出任何問題都是有可能的,當初合約就沒說要保證安全執行緒(客戶一定會抓狂)。

為什麼要用SingleTon?

    不知道大家有沒有想過,當初為什麼要把這個類型用SingleTon來做。

    我看到的大多數答案是節省資源和全域狀態,固定演算法的介面適配(不排除還有更好的答案)。

    先討論節省資源的問題,首先不new執行個體一定比new執行個體要節省資源(CPU和記憶體),這點不用質疑,但是如果要做到安全執行緒,似乎就要再考量一下了。

    如果SingleTon執行個體本身的實現方式就保證了安全執行緒(僅使用堆棧和參數中的對象,對自身引用的對象唯讀),那麼安全執行緒是0代價的。

    如果其實現方式涉及Lock等同步,那麼衝突機率是多少,如果衝突機率足夠的高,那麼大多數時間軸程將進入等待狀態,導致大量佔用時間資源和CPU資源。即使衝突機率很低,由於Lock需要同步Cache和記憶體,所以一樣需要花費一些額外的代價(同步Cache的時間代價)。

    說到這裡,還覺得有Lock的SingleTon一定比new執行個體節省資源?未必吧,到底誰省資源還是要具體問題具體分析一下,不Profiling一下,誰又知道結果哪?

    至於全域狀態,可以的話,要盡量避免全域狀態的使用,如果必須要使用的話,確實沒什麼好的方案,不過,建議作為全域狀態使用的SingleTon需要保證所有成員的安全執行緒(否則,一個team member的小錯誤,就可能導致全域狀態出錯)。

    第三種固定演算法的介面適配,這是我比較提倡的SingleTon用法。這裡涉及幾個部分的要求:

  1. 演算法固定
  2. 演算法不存在狀態,所有的變化均來自參數,且實現無需線程同步就可以保證安全執行緒
  3. 接收方要求符合某介面(泛指)

    如果第一點不滿足,那就不可能作為SingleTon存在。第二點是確保使用SingleTon的效能優勢。而第三點是OO立場上的SingleTon必要性,否則為什麼不用靜態方法。

    也許這3點比較抽象,舉個實際點的例子:

public class PersonNameComparer    : IComparer<Person>{    public int Compare(Person x, Person y)    {        if (x == y)            return 0;        if (x == null)            return -1;        if (y == null)            return 1;        return string.Compare(x.name, y.name);    }}

    第一,演算法固定,就是比較2個人的名字,沒有第二種演算法,要是有那也是其它類的職責;第二,不存在狀態,只用了x和y兩個參數,沒有線程同步;第三,因為需要在排序的場合需要IComparer<Person>介面的執行個體,因此不能使用靜態方法(假設不能使用委託)。

    因此,這個類型如果是SingleTon的話,那將是合理的(當然這裡不用SingleTon也沒問題)。

後話

    N年前,我去面試某公司時,某面試官問我資料庫聯結能不能做SingleTon,我說不能,會引入很多問題,結果面試官很不滿意。

    N月前,面試某人,在談到設計模式用在什麼地方時候,舉例說在Web項目中把一個WebService的代理類做成了SingleTon,結果我很不滿意。

    哎,SingleTon啊SingleTon,真是GoF引入OO的最大的坑。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.