單例模式中的雙重校驗鎖


// 單線程的時候
class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

}

這段在使用多線程的情況下無法正常工作。在多個線程同時調用getHelper()時,必須要獲取,否則,這些線程可能同時去創建對象,或者某個線程會得到一個未完全初始化的對象。

鎖可以通過代價很高的同步來獲得,就像下面的例子一樣。

//這樣寫雖然正確,但是粗暴地把getHelper鎖住了,這樣代價很大
class Foo {
    private Helper helper = null;
    public synchronized Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

}

只有getHelper()的第一次調用需要同步創建對象,創建之后getHelper()只是簡單的返回成員變量,而這里是無需同步的。 由於同步一個方法會降低100倍或更高的性能[2], 每次調用獲取和釋放鎖的開銷似乎是可以避免的:一旦初始化完成,獲取和釋放鎖就顯得很不必要。許多程序員一下面這種方式進行優化:

  1. 檢查變量是否被初始化(不去獲得鎖),如果已被初始化立即返回這個變量。
  2. 獲取鎖
  3. 第二次檢查變量是否已經被初始化:如果其他線程曾獲取過鎖,那么變量已被初始化,返回初始化的變量。
  4. 否則,初始化並返回變量。
//這種雙重檢查鎖定就很好地解決問題,避免每次調用都要獲取鎖
class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM