單例模式:用來創建獨一無二的,只能夠有一個實例的對象。 單例模式的結構是設計模式中最簡單的,但是想要完全實現一個線程安全的單例模式還是有很多陷阱的,所以面試的時候屬於一個常見的考點~
單例模式的應用場景:有一些對象其實只需要一個,比如:線程池,緩存,對話框,處理偏好設置和註冊表的對象,日誌對象,充當打印機,顯卡等設備的驅動程序對象。這些對象只能夠擁有一個實例,如果創建出了多個實例,就會導致一些程序的問題。程序的行爲異常,資源使用的過量,或者導致不一致的結果。常用來管理共享的資源,比如數據庫的連接或者線程池。
單例模式的類圖非常簡單,如下~,並且經典的實現也非常的簡單。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Singleton
{ public : static Singleton*
getInstance(); //析構的時候釋放資源~ ~Singleton()
{ if (
(_instance != NULL) delete _instance; } protected : Singleton(); private : static Singleton*
_instance; } Singleton
*Singleton::_instance = NULL; Singleton*
Singleton::getInstance() { if (
_instance == NULL) { _instance
= new Singleton(); } return _instance; } |
經典的實現非常容易,但是存在一個問題,就是這個經典的實現非線程安全,多線程的情況下,這個單例模式的實現會出現問題~,如何解決呢?改進!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Lock { private : mutex
mtex; public : Lock(mutex
m) : mtex(m) { mtex.Lock(); } ~Lock() { mtex.Unlock(); } }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Singleton
{ public : static Singleton*
getInstance(); //析構的時候釋放資源~ ~Singleton()
{ if (
(_instance != NULL) delete _instance; } protected : Singleton(); private : static Singleton*
_instance;<br> static mutex
m; } Singleton
*Singleton::_instance = NULL; Singleton*
Singleton::getInstance() {<br> //check
之前進行臨界區加鎖操作<br> Lock lock(m); if (
_instance == NULL) { _instance
= new Singleton(); } return _instance; } |
線程安全保證的一種方法及爲在check _instance == NULL 之前進行臨界區加鎖,如果已經有一個線程進入訪問,其他線程必須等待,這樣就能夠保證多線程情況下實例的唯一!
but,互斥的同步會導致性能的降低,即使_instance已經不爲空了,每次還是需要加鎖,這樣操作花費就比較多,性能必定比較差。
另外還有一些比較好的方法:
1.(非線程同步的方法)上面的操作均爲一種lazy initialization的思想,及用到的時候在初始化,這樣程序效率比較高,但是有一個另外比較好的方法可以採用是提前初始化,將_instance設置爲static之後直接初始化爲Singleton對象,每次只需要執行返回操作即可。
這樣的話同樣會導致問題,就是如果單例本來資源比較多,但是不需要創建那麼早,就會消耗資源~。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Singleton
{ public : static Singleton*
getInstance(); //析構的時候釋放資源~ ~Singleton()
{ delete _instance; } protected : Singleton(); private : static Singleton*
_instance; } Singleton
*Singleton::_instance = new Singleton(); Singleton*
Singleton::getInstance() { return _instance; } |
2.另外一種提升因爲同步導致的性能變差的方法稱爲“雙重檢驗加鎖”。方法如下:
思路是只有在第一次創建的時候進行加鎖,當_instance不爲空的時候就不需要進行加鎖的操作,這樣就可以提升性能~
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
41
|
class Lock { private : mutex
mtex; public : Lock(mutex
m) : mtex(m) { mtex.Lock(); } ~Lock() { mtex.Unlock(); } }; class Singleton
{ public : static Singleton*
getInstance(); //析構的時候釋放資源~ ~Singleton()
{ if (
(_instance != NULL) delete _instance; } protected : Singleton(); private : static Singleton*
_instance; static mutex
m; } Singleton
*Singleton::_instance = NULL; Singleton*
Singleton::getInstance() { //check
之前進行臨界區加鎖操作 //雙重檢驗加鎖 if (_instance
== NULL ) { Lock
lock(m); if (
_instance == NULL) { _instance
= new Singleton(); } } return _instance; } |
總之,小小單例模式問題還是挺多的,面試官喜歡問的一個問題~,因爲還是有很多陷阱的。