單例模式全面學習

單例模式:用來創建獨一無二的,只能夠有一個實例的對象。 單例模式的結構是設計模式中最簡單的,但是想要完全實現一個線程安全的單例模式還是有很多陷阱的,所以面試的時候屬於一個常見的考點~

單例模式的應用場景:有一些對象其實只需要一個,比如:線程池,緩存,對話框,處理偏好設置和註冊表的對象,日誌對象,充當打印機,顯卡等設備的驅動程序對象。這些對象只能夠擁有一個實例,如果創建出了多個實例,就會導致一些程序的問題。程序的行爲異常,資源使用的過量,或者導致不一致的結果。常用來管理共享的資源,比如數據庫的連接或者線程池。

單例模式的類圖非常簡單,如下~,並且經典的實現也非常的簡單。

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;
}

 

總之,小小單例模式問題還是挺多的,面試官喜歡問的一個問題~,因爲還是有很多陷阱的。

發佈了8 篇原創文章 · 獲贊 27 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章