設計模式 1/23 單例模式

爲何將單例模式排名第一,很簡單,面試的時候聊到設計模式,大機率就從單例模式開始入手,循循漸進。面試

固然,咱們學習單例模式不是爲了去應付面試,而是學好設計模式後,融匯貫通,應用於咱們的設計,開發,項目中。數據庫

單例模式是最簡單的設計模式之一設計模式

單例模式【Singleton Pattern】:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。緩存

兩層含義:1,有且僅有一個實例安全

     2,有一個訪問他的全局訪問點多線程

咱們來想象一個場景,放眼全球,全部的黨派,都只有一個主席(這應該沒有例外吧)這個案例,若是要拜訪他,是否是須要一個全局的訪問點。ide

對於一個類來講,咱們怎樣保證他僅有一個實例,初步想到的是不許外部實例化,由於若是能夠外部實例化,我就能夠實例化無數個。那不許外部實例化,就只能內部了,那就動手修改內部的構造方法。函數

(敲黑板,劃重點) 全部類都有構造方法,不編碼則系統默認生成空的構造方法,如有顯示定義的構造方法,默認的構造方法就會失效學習

如此,咱們只須要顯示的修改構造方法便可,既然不許外部調用,那咱們用private修飾構造方法便可優化

第一步,控制外部實例化咱們就作到了

    /// <summary>
    /// 黨派
    /// </summary>
    public class Party
    {
        /// <summary>
        /// 私有化構造函數,不許外部實例化
        /// </summary>
        private Party()
        {
        }
    }
}
View Code

那咱們須要一個主席,一個黨派總不能羣龍無首吧,因而咱們定義一個主席,而且咱們要提供一個訪問的點吧,否則別人想和主席聊兩句都找不到人

第二步,建立一個主席,並提供一個訪問點

    /// <summary>
    /// 黨派
    /// </summary>
    public class Party
    {
        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman;

        /// <summary>
        /// 私有化構造函數,不許外部實例化
        /// </summary>
        private Party()
        {
        }

        /// <summary>
        /// 找主席
        /// </summary>
        /// <returns></returns>
        public static Party GetChairman()
        {
            if (_chairman == null)//主席不存在則建立一個主席
            {
                _chairman = new Party();
            }
            return _chairman;
        }
    }
}
View Code

哇哇哇~!!!私有構造函數,爲何這裏能夠調用啊,不是不讓調用麼!!!這是同一個類中,同一個類中,私有,公有,保護,均可以隨意調用。

咱們再讓主席講一句話

    /// <summary>
    /// 黨派
    /// </summary>
    public class Party
    {
        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman;

        /// <summary>
        /// 私有化構造函數,不許外部實例化
        /// </summary>
        private Party()
        {
        }

        /// <summary>
        /// 找主席
        /// </summary>
        /// <returns></returns>
        public static Party GetChairman()
        {
            if (_chairman == null)//主席不存在則建立一個主席
            {
                _chairman = new Party();
            }
            return _chairman;
        }

        public void Say()
        {
            Console.WriteLine("同志們好!");
        }
    }
View Code

這樣咱們的一個簡單的單單例模式就算完成了,咱們再看看如何調用

 class Program
    {
        static void Main(string[] args)
        {
            Party chairman = Party.GetChairman();
            chairman.Say();
        }
    }
View Code

那麼問題來了,單線程操做的時候,以上沒有問題,那多線程的時候呢,多我的同時訪問主席,會不會可能產生多個主席呢,多個主席,是否是就違反了單例模式最基本的原則,僅有一個實例呢。多個主席,難道打一架,勝者爲王麼?

因而咱們須要再次優化咱們的代碼

    /// <summary>
    /// 黨派
    /// </summary>
    public class Party
    {
        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman;

        /// <summary>
        /// 靜態只讀的進程輔助對象
        /// </summary>
        private static readonly object syncRoot = new object();

        /// <summary>
        /// 私有化構造函數,不許外部實例化
        /// </summary>
        private Party()
        {
        }

        /// <summary>
        /// 找主席
        /// </summary>
        /// <returns></returns>
        public static Party GetChairman()
        {
            lock (syncRoot)//鎖你丫的
            {
                if (_chairman == null)//主席不存在則建立一個主席
                {
                    _chairman = new Party();
                }
            }
            return _chairman;
        }

        public void Say()
        {
            Console.WriteLine("同志們好!");
        }
    }
}
View Code

以上,再同一個時刻加了鎖的那部分代碼,只有一個線程能夠進入。

若是lock不懂,我知道大家懶

lock 確保當一個線程位於代碼的臨界區時,另外一個線程不進入臨界區。若是其餘線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放

 

那麼問題又來了,爲何每一次都要鎖,爲何,見一次主席那麼那麼難麼?能不能沒有建立的時候我才鎖,有主席的時候我直接訪問啊

固然能夠,這就是雙重鎖定

    /// <summary>
    /// 黨派
    /// </summary>
    public class Party
    {
        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman;

        /// <summary>
        /// 靜態只讀的進程輔助對象
        /// </summary>
        private static readonly object syncRoot = new object();

        /// <summary>
        /// 私有化構造函數,不許外部實例化
        /// </summary>
        private Party()
        {
        }

        /// <summary>
        /// 找主席
        /// </summary>
        /// <returns></returns>
        public static Party GetChairman()
        {
            if (_chairman == null) //主席不存在,我再鎖定,進行選舉
            {
                lock (syncRoot) //鎖你丫的
                {
                    if (_chairman == null) //主席不存在則建立一個主席
                    {
                        _chairman = new Party();
                    }
                }
            }
            return _chairman;
        }

        public void Say()
        {
            Console.WriteLine("同志們好!");
        }
    }
}
View Code

這裏咱們就先判斷了主席是否存在,不存在,咱們再閉門選舉,等咱們選舉出來了,你再來訪問。

咱們既優化了執行,也保證了僅有一個主席,保證了多線程訪問的安全性。

其實還有什麼餓漢式,懶漢式的區別

從字面理解

餓漢,一開始我就實例化本身

        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman = new Party();

懶漢,被引用時候,才實例化本身

        /// <summary>
        /// 我是黨派主席
        /// </summary>
        private static Party _chairman ;
View Code

具體使用哪種,應該根據實際狀況來定,無優劣之分,值得注意的是線程安全


總結下

優勢:

一、在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例。

二、避免對資源的多重佔用(好比寫文件操做)。

缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

使用場景: 

一、要求生產惟一序列號。

二、計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。

三、建立的一個對象須要消耗的資源過多,好比 I/O 與數據庫的鏈接等。

以上就是關於單例模式的分享

一路前行,風雨無阻

相關文章
相關標籤/搜索