設計模式——懶漢式單例類PK餓漢式單例類

周麗同發表於2015-12-20

前言

   我們都知道生活中好多小軟體,有的支援多IP線上,有的僅僅侷限於單個IP線上。為什麼這樣設計,在軟體開發階段就是,有需求就是發展。這就是軟體開發的一個設計模式——懶漢式單例類和餓漢式單例類。

內容

   現在的網際網路發展很迅速,人們對保護自己隱私的意識也日益提高。所以單例模式就上場了,且看:

單例模式

定義:

  單例模式保證一個類僅有一個例項,並提供了一個訪問它的全域性點。

解釋:

  用大白話來說就是 一條路,一次只讓一個過,相當於種蘿蔔,一個坑只能種一個蘿蔔。官方講通常我們可以讓一個全域性變數使得一個物件被訪問,但它不能防止你例項化多個物件。一個最好的方法就是,讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項可以被建立,並且可以他可以提供一個訪問該例項的方法。

     看圖:

 

程式碼也灰常簡單哦:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//作者:周麗同
//檔案:單例模式

namespace ConsoleApplication2
{
    class Singleton
    {
        private static Singleton instance;

        private Singleton ()//構造方法讓其為private,這就堵死了外界利用new建立此類例項的可能;
        {

        }

        public static Singleton GetInstance()//此方法時獲得本類例項的唯一全域性訪問點;
        {
            if (instance ==null )//若例項不存在,則new一個新的例項,否則返回已有的例項;
            {
                instance = new Singleton();
            }

            return instance;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//作者:周麗同
//檔案:單例模式

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Singleton s1 = Singleton.GetInstance();
            Singleton s2 = Singleton.GetInstance();

            if (s1==s2 )//比較兩次例項化後物件的結果是例項相同;
            {
                Console.WriteLine("兩個物件是相同的例項。");
            }

            Console.Read();
        }
    }
}

好處:

   單例模式因為Singleton類封裝它的唯一例項,這樣它可以嚴格地控制客戶是怎樣訪問以及何時訪問它。就是對唯一例項的受控訪問。

完善單例——多執行緒

   對於單例模式,我們只是注意到了在例項化的時候防止例項化氾濫,但是考慮細節問題,多執行緒的程式中,同時訪問Singleton類,呼叫裡面的GetInstance()方法,也會有可能造成建立多個例項的現象,給我防止這個,可以給程式加一把鎖來處理,相當於一個屋子只能進去一個人去完成任務,第一個人進去後把門鎖了,防止後面的人去屋子裡面完成在該時段的同樣的任務。

見程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//作者:周麗同
//檔案:多執行緒時的單例
namespace 單例模式之懶漢與餓漢
{
    class Singleton
    {
        private static Singleton instance;
        private static readonly object syncRoot = new object();
        //程式執行時建立一個靜態只讀的程式輔助物件;
        private Singleton ()
        {

        }

        public static Singleton GetInstance()
        {
            lock (syncRoot )//在同一個時刻加了鎖的那部分程式只有一個執行緒可以進入;
            {
                if (instance ==null )
                {
                    instance =new Singleton ();
                }
            }

            return instance;
        }
    }
}

   這裡解釋一下,Lock是確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區。如果其他執行緒試圖進入鎖定的程式碼,則它將一直等待(即被阻止),直到該物件被釋放。由於有了 lock,就保證了多執行緒下同上訪問也不會造成多例項的生成。

   多執行緒雖然解決了保證例項化一個。但是對於加鎖的方式上不管有沒有例項化物件都是先加了鎖,這樣看起來未免有點不合理。雙重鎖定解決了這種情況。

完善單例——雙重鎖定

   它先是判斷例項是否存在,不存在再枷鎖處理。這樣我們不用讓執行緒每次都加鎖,而只是在例項未被建立的時候再加鎖處理,同時這樣也能保證多執行緒的安全。這就是我們所說的Double-Check Locking。至於為什麼判斷Instance是否為空兩次,第一次是判斷是否有加鎖的畢業,第二次是針對第二次以後進來的執行者是否執行例項化的判斷。

見程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//作者:周麗同
//檔案:雙重鎖定

namespace 雙重鎖定
{
    class Singleton
    {
        private static Singleton instance;
        private static readonly object syncRoot = new object();
        private Singleton()
        {

        }

        public static Singleton GetInstance()
        {
            if (instance ==null )//先判斷例項是否存在,不存在再加鎖處理;
            {
                lock (syncRoot )
                {   
                    //當instance不存在的情況下,當instance為 null並且同時有兩個執行緒呼叫GetInstance()方法時;
                    //都可以通過第一重instance==null的判斷。然後由於lock機制,這兩個執行緒則只有一個進入,另一
                    //個在外排隊等候。等另一個出來以後,另一個才能進入,如果沒有第二重的instance==null,還是
                    //可以重新建立例項的。沒有達到單例的目的。
                    if  (instance ==null )
                    {
                        instance = new Singleton();
                    }
                }
            }

            return instance;
        }
    }
}

靜態初始化

   C#與公共語言執行庫也提供了一種“靜態初始化”方法,這種方法不需要開發人員顯式地編寫執行緒安全程式碼,即可解決多執行緒環境下它是不安全的問題。

見程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//作者:周麗同
//檔案:靜態初始化

namespace 靜態初始化
{
    public sealed   class Singleton//防止發生派生,而派生可能會增加例項;
    {
        //在第一次引用類的任何成員時建立例項。公共語言執行庫負責處理變數初始化;
        private static readonly Singleton instance = new Singleton();
        private Singleton ()
        {

        }

        public static Singleton GetInstance()
        {
            return instance;
        }
    }
}

   這個也實現了全域性訪問和例項化控制,公共靜態屬性訪問例項提供了一個全域性訪問點。不同之處在於它利用公共語言執行庫來初始化變數。構造方法和前面一樣都是私有的,不能再類本身以外例項化Singleton類;其中instance變數標記為readonly(只讀),表示只能在靜態初始化期間或在類建構函式中分配變數。

總結

   靜態初始化的方式是在自己載入的時候就將自己例項化,需要提前佔用資源,所以被形象的稱之為餓漢式單例類;原先的單例模式處理方式要在第一次被引用時,才會將自己例項化,面臨著多執行緒訪問的安全性問題,需要做雙重鎖定這樣的處理才可以保證安全,所以為懶漢式單例類。本人菜鳥一枚,如果不合適的地方,望大神斧正。

1、對比著學習更加容易理解知識。

2、永遠不要相信一件東西可以近乎完美,只要發現總有更好的。

3、多做總結,多多回顧。


最後的最後,感謝您的寶貴時間~~~

相關文章