面試:一個單例模式,足以把你秒成渣

去面試(對,又去面試)面試

問:單例模式瞭解吧,來,拿紙和筆寫一下單例模式。編程

我心想,這TM不是瞧不起人嗎?我編程十年,能不知道單例模式。設計模式

答:(.net 平臺下)單例模式有兩種寫法:併發

第一種:飢餓模式,關鍵點,static readonly函數

public static readonly SingletonSimple Instance = new SingletonSimple();

第二種:懶加載模式,關鍵點,lock + 兩次判斷spa

        static readonly object locker = new object();
        static SingletonLazy singleton = null;
        public static SingletonLazy Instance
        {
            get
            {
                if (singleton == null)
                {
                    lock (locker)
                    {
                        if (singleton == null)
                        {
                            singleton = new SingletonLazy();
                        }
                    }
                }

                return singleton;
            }
        }

 我再贈送你一種,第三種:經過IOC容器,注入單例。.net

 

問:這兩種方式(第一種和第二種)有什麼不一樣嗎?(好戲開始)線程

答: 懶加載模式的單例是在類調用時進行建立。飢餓模式下的單例在程序啓動時建立(這裏錯了),浪費資源。設計

彷佛答案就是這樣,好些網文,博主也都是這麼寫的,但你們都錯了。(輕信他人,不本身思考,這麼基礎的東西竟然沒搞明白)code

反饋:兩種方式並無本質的區別,都是在類調用的時候建立

尚未完,虐狗模式纔剛剛開始。

問:說一下lock的原理

答:對代碼塊加鎖,加鎖的代碼只容許串行執行,防止併發衝突。lock本質上是經過 System.Threading.Monitor實現的,但lock使用比Monitor更簡單,能夠自動釋放。

問:那Monitor是如何實現多個線程的阻塞調用的?一個線程執行完,是如何通知下一個線程執行的?有沒有本身實現過一個lock(不使用.net自帶的lock)?

答:......(徹底一臉懵逼,根本不知道怎麼回答)

問:IOC使用了什麼設計模式,IOC是如何控制對象生命週期的?

答:......(還沒從剛纔的窘迫中反應過來,更是不知道該說什麼)

 

本身十年的工做經驗,就這麼敗在了一個單例模式上。(T﹏T)

 

回家以後,本身作了實驗,證明兩種方式確實都是在類被調用的時候纔會建立單例對象。

public static readonly 建立的單例

public class SingletonSimple
    {
        SingletonSimple()
        {
            Console.WriteLine($"Singleton Simple Create"); } public static readonly SingletonSimple Instance = new SingletonSimple(); public void Work() { Console.WriteLine("Singleton Simple Work"); } }

 

lock + 兩次判斷 建立的單例

public class SingletonLazy
    {
        SingletonLazy()
        {
            Console.WriteLine($"Singleton Lazy Create"); } static readonly object locker = new object(); static SingletonLazy singleton = null; public static SingletonLazy Instance { get { if (singleton == null) { lock (locker) { if (singleton == null) { singleton = new SingletonLazy(); } } } return singleton; } } public void Work() { Console.WriteLine("Singleton Lazy Work"); } }

 

main函數

    class Program
    {
        static void Main(string[] args) { Console.WriteLine("begin ..."); SingletonLazy.Instance.Work(); SingletonSimple.Instance.Work(); Console.WriteLine("end ..."); Console.Read(); } }

 

輸出結果以下 :

begin ...
Singleton Lazy Create
Singleton  Lazy Work
Singleton Simple Create
Singleton Simple Work
end ...

 

咱們看,若是飢餓模式單例在程序啓動就自動加載的話,應該會先輸出「Singleton Simple Create」,但實際並非這樣,而且我屢次調整main函數中的單例調用順序,觀察結果,能夠得出結論,兩種方式並無區別,都是在調用時加載的。

悔恨啊,竟然栽在這麼個小問題上,顏面掃地。

謹記:基礎原理,獨立思考,真的很重要。

相關文章
相關標籤/搜索