1. 程式人生 > >單例模式 (懶漢式, 執行緒同步詳解)

單例模式 (懶漢式, 執行緒同步詳解)

單例模式(懶漢式)

在懶漢式寫法中, 我們需要非常注意執行緒同步的問題. 大概有一下幾個:
1. getInstance() 直接鎖方法好不好
2. 雙重鎖定
3. synchronized(this)行不行

1. getInstance() 直接鎖方法好不好

這種寫法:

class Singleton {
    private static Singleton instance;
    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance == null
){ instance = new Singleton(); } return instance; } }

我們可以看到這裡synchronized是鎖方法, 當兩個執行緒都要進入getInstance()時, 只有一個能進入, 並創建出例項, 然後另外一個進入後, 判斷 instace不為null, 然後直接得到instance. 這種做法是沒有錯誤的. 但是由於執行緒都需要通過getInstance()來獲取物件, 所以getInstance()呼叫頻率很高, 所以執行緒被鎖的頻率也很高, 所以這種做法效率低.

2. 雙重鎖定

由於上面效率的原因, 你可能就會想到把 syschronized 放在 getInstance()裡面, 這種可避免在呼叫getInstance()時的阻塞問題, 如下:

class Singleton {
    private static Singleton instance;
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                instance = new
Singleton(); } } return instance; } }

這種寫法看似沒有問題, 其實卻有一個很大的隱患, 在於: 如果兩個執行緒同時執行getInstance(),判斷 instance都不為null後, 進入if判斷語句. 這個時候一個執行緒獲得鎖, 然後進入new了一個物件, 並開心的執行完了. 這個時候另外一個執行緒獲得了鎖, 但讓它也不會再去判斷 instace是否為null, 所以它也會再執行一次new操作. 所以這裡執行了兩次new操作. 當然最後instance還是隻指向後一次new的物件.
所以這個時候需要雙重鎖定, 就是在 synchronized中再加一次 null判斷, 如下:

class Singleton {
    private static Singleton instance;
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

這樣就可以保證不會new兩次, 也是相對比較正確的, 並且效率也很高.

3. synchronized(this)行不行

答案是不行的, 如果你寫程式碼看一看, 直接就提示語法錯誤了, 因為我們的 getInstance() 方法是 static的, 所以裡面不能使用 this.

好了以上三點終結, 希望對大家有幫助.