1. 什麼是單例
保證一個類只有一個實例的實現方法。
2. 應用場景
當一個類只能有一個實例而且客戶可以從一個衆所周知的訪問點訪問它時。
對有些類來說,只有一個實例很重要,如線程池,註冊表,文件系統等,雖然全局變量也可以提供全局訪問點,但是不能防止你實例化多個對象
頁面訪問計數器
需要保持狀態的工具類
需求很多,不能一一列舉了
3. 實現方法
一個私有,兩個靜態
一個私有
就是私有構造函數
單例模式的思想就是一個類只有一個實例,即外部任何類都不能實例化該類,那麼什麼樣的類外部不能實例化呢?我們知道,實例化一個類的時候,需要調用構造函數,而一般構造函數都是public的,所以能夠被外部調用,所以能夠在外部實例化,當將構造函數設置爲private時,外部就不能調用類的構造函數了,也就不能實例化該類了,該類只能在類的內部實例化。這個思想是實現單例模式的關鍵。
兩個靜態
1.靜態成員變量uniqueInstance,該成員變量就是類的唯一實例
2.靜態方法GetInstance(),用來獲取該類的唯一實例
前面提到了使用私有構造函數是實現單例模式的關鍵,那麼下面的問題就是怎麼在外部獲取該單例呢?由於任何外部類都不能實例化該類,所以我們無法通過使用new一個類的對象來調用類裏面的方法獲取單例 ( 即不能通過
Singleton singleton = new Singleton();
singleton.GetInstance()
來獲取單例 ),只能通過類裏面的靜態方法,通過類名調用靜態方法(
Singleton.GetInstance()
)來獲取單例,而靜態方法只能調用靜態成員,所以類的成員變量也必須是靜態的。
4. 不同場景的適用方法
非線程安全
1 /// <summary>
2 /// 單例模式的實現
3 /// </summary>
4 public sealed class Singleton
5 {
6 //定義一個靜態變量來保存類的實例
7 private static Singleton _instance = null;
8
9 //定義私有構造函數,使外界不能創建該類實例
10 private Singleton()
11 {
12 }
13 /// <summary>
14 /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點
15 /// </summary>
16 /// <returns></returns>
17 public static Singleton Instance()
18 {
19 //如果類的實例不存在則創建,否則直接返回
20 if (_instance == null)
21 {
22 _instance = new Singleton();
23 }
24 return _instance;
25 }
26 }
上面的單例模式的實現在單線程下確實是可以的,但是在多線程環境下會存在兩個線程同時執行if (instance == null)
並且創建兩個不同的實例
簡單線程安全
1 /// <summary>
2 /// 單例模式的實現
3 /// </summary>
4 public sealed class Singleton
5 {
6 // 定義一個靜態變量來保存類的實例
7 private static Singleton instance = null;
8
9 // 定義一個標識確保線程同步
10 private static readonly object padlock = new object();
11
12 Singleton()
13 {
14 }
15
16 public static Singleton Instance()
17 {
18 // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖",
19 // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖
20 // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖"
21 lock (padlock)
22 {
23 // 如果類的實例不存在則創建,否則直接返回
24 if (instance == null)
25 {
26 instance = new Singleton();
27 }
28 }
29 return instance;
30 }
31 }
上面的例子解決了多線程的問題,但是每個線程調用Instance()都會使用到鎖,而調用鎖的開銷較大,這個實現會有一定的性能損失。
雙重驗證線程安全
1 /// <summary>
2 /// 單例模式的實現
3 /// </summary>
4 public sealed class Singleton
5 {
6 // 定義一個靜態變量來保存類的實例
7 private static Singleton instance = null;
8
9 // 定義一個標識確保線程同步
10 private static readonly object padlock = new object();
11
12 Singleton()
13 {
14 }
15
16 public static Singleton Instance()
17 {
18 // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖",
19 // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖
20 // lock語句運行完之後(即線程運行完之後)會對該對象"解鎖"
21 if (instance == null)
22 {
23 lock (padlock)
24 {
25 // 如果類的實例不存在則創建,否則直接返回
26 if (instance == null)
27 {
28 instance = new Singleton();
29 }
30 }
31 }
32 return instance;
33 }
34 }
上面的例子在保證線程安全的同時提高了性能
靜態變量實現單例
1 /// <summary>
2 /// 單例模式的實現
3 /// </summary>
4 public sealed class Singleton
5 {
6 //在Singleton第一次被調用時會執行instance的初始化
7 private static readonly Singleton instance = new Singleton();
8
9 private Singleton()
10 {
11 }
12
13 public static Singleton Instance()
14 {
15 return instance;
16 }
17 }
上面的例子利用.net的特性來完成單例模式的創建,也是線程安全的
5. 單例模式的優點
在內存中只有一個對象,節省內存空間;
避免頻繁的創建銷燬對象,可以提高性能;
避免對共享資源的多重佔用,簡化訪問;
爲整個系統提供一個全局訪問點。