關於java中的double check lock

scugxl發表於2017-09-07

##實現一個正確的單例模式

在熟悉的單例模式中你或許會遇到下面的方式來實現一個單例:

// version 1
class Singleton {
    private static Singleton _INSTANCE


    static Singleton getInstance() {
        if (_INSTANCE == null) {
            _INSTANCE = new Singleton()
        }
        return _INSTANCE;
    }

}

但是這個在多執行緒環境下會有問題:
Problem 1: 這個會建立多個Singleton物件.

那麼我可以加上下面的同步就可以了:

// version 2
class Singleton {
    private static Singleton _INSTANCE


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

}

這是一個完全正確的版本, 除了效能比較差.


那麼我們可以直接不使用lazy init, 就可以不需要同步了:

// version 3
class Singleton {
    private static final Singleton _INSTANCE = new Singleton()
    static Singleton getInstance() {
        return _INSTANCE;
    }
}

這裡final不是必須的.
static為我們提供了保證(正確的被建立, 建立的物件是完整的) 可以參考:JSR 133 (Java Memory Model) FAQ

恩 這很不錯, 除了 也許我根本不需要它, 但是有可能我們需要用到的時候才建立.

所以, 便引出我們今天的雙重檢查版本:

// version 4
class Singleton {
    private static Singleton _INSTANCE


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

}

這個程式碼是無法工作的. 因為這個可能讓其他執行緒看到沒有完全構件好的物件:

// 原始程式碼
_INSTANCE = new Singleton()

// 實際上的步驟:
1.allocateMemory -> object 
2.Singleton._INSTANCE = object
3.init object attributes

實際上我們對2,3 的步驟是無法保證,
也就是如果2先執行 (指令重排), 那麼其他執行緒可能看到構建了一半的物件.

所以常見的可以在多執行緒下正確工作的單例模式:

  1. static init
  2. object holder
  3. synchronized 在class上同步
  4. 把instance標記為volatile. 因為volatile會禁止上面的重排(>=JDK1.4).
//object holder 
private static class LazySomethingHolder {
  public static Something something = new Something();
}

public static Something getInstance() {
  return LazySomethingHolder.something;
}

Reference:
1.https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
2.https://www.ibm.com/developerworks/java/library/j-dcl/index.html

相關文章