設計模式(1)——單例模式

原文地址:http://blog.jobbole.com/78059/

單例模式也就是保證一個類只有一個實例的一種實現方法;

應用場景:

 單例模式的使用自然是當我們的系統中某個對象只需要一個實例的情況,例如:操作系統中只能有一個任務管理器,操作文件時,同一時間內只允許一個實例對其操作等,既然現實生活中有這樣的應用場景;

實現方式:

類定義私有的構造函數(就不能在外界通過new創建實例了)來確保一個類只有一個實例;定義靜態私有變量保存此實例(每個線程都有自己的線程棧,定義爲靜態主要是爲了在多線程確保類有一個實例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
    /// 單例模式的實現
    /// </summary>
    public class Singleton
    {
        // 定義一個靜態變量來保存類的實例
        private static Singleton uniqueInstance;
 
        // 定義私有構造函數,使外界不能創建該類實例
        private Singleton()
        {
        }
 
        /// <summary>
        /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            // 如果類的實例不存在則創建,否則直接返回
            if (uniqueInstance == null)
            {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }

上面的單例模式的實現在單線程下確實是完美的,然而在多線程的情況下會得到多個Singleton實例,因爲在兩個線程同時運行GetInstance方法時,此時兩個線程判斷(uniqueInstance ==null)這個條件時都返回真,此時兩個線程就都會創建Singleton的實例,這樣就違背了我們單例模式初衷了,既然上面的實現會運行多個線程執行,那我們對於多線程的解決方案自然就是使GetInstance方法在同一時間只運行一個線程運行就好了,也就是我們線程同步的問題了,具體的解決多線程的代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// <summary>
    /// 單例模式的實現
    /// </summary>
    public class Singleton
    {
        // 定義一個靜態變量來保存類的實例
        private static Singleton uniqueInstance;
 
        // 定義一個標識確保線程同步
        private static readonly object locker = new object();
 
        // 定義私有構造函數,使外界不能創建該類實例
        private Singleton()
        {
        }
 
        /// <summary>
        /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖",
            // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖
            // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖"
            lock (locker)
            {
                // 如果類的實例不存在則創建,否則直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new Singleton();
                }
            }
 
            return uniqueInstance;
        }
    }

上面這種解決方案確實可以解決多線程的問題,但是上面代碼對於每個線程都會對線程輔助對象locker加鎖之後再判斷實例是否存在,對於這個操作完全沒有必要的,因爲當第一個線程創建了該類的實例之後,後面的線程此時只需要直接判斷(uniqueInstance==null)爲假,此時完全沒必要對線程輔助對象加鎖之後再去判斷,所以上面的實現方式增加了額外的開銷,損失了性能,爲了改進上面實現方式的缺陷,我們只需要在lock語句前面加一句(uniqueInstance==null)的判斷就可以避免鎖所增加的額外開銷,這種實現方式我們就叫它 “雙重鎖定”,下面具體看看實現代碼的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/// <summary>
    /// 單例模式的實現
    /// </summary>
    public class Singleton
    {
        // 定義一個靜態變量來保存類的實例
        private static Singleton uniqueInstance;
 
        // 定義一個標識確保線程同步
        private static readonly object locker = new object();
 
        // 定義私有構造函數,使外界不能創建該類實例
        private Singleton()
        {
        }
 
        /// <summary>
        /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖",
            // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖
            // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖"
            // 雙重鎖定只需要一句判斷就可以了
            if (uniqueInstance == null)
            {
                lock (locker)
                {
                    // 如果類的實例不存在則創建,否則直接返回
                    if (uniqueInstance == null)
                    {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章