Java併發程式設計實戰--雙重檢查加鎖( double check lock)與延遲初始化佔位

衣舞晨風發表於2017-06-11

DCL

     在任何一本介紹併發的書中都會討論聲名狼藉的雙重檢查加鎖(DCL)。
     下圖來自:http://zoroeye.iteye.com/blog/2058889
這裡寫圖片描述

     DCL的真正問題在於:當在沒有同步的情況下讀取一個共享物件時,可能發生的最糟糕的事情只是看到一個失效值(在這種情況下是一個空值),此時DCL方法將通過在持有鎖的情況下在此嘗試來避免這種風險。然而,實際情況遠比這種情況糟糕——執行緒可能看到引用的當前值,但物件的狀態值卻是失效的,這意味著執行緒可以看到物件處於無效或錯誤的狀態。

     在JVM的後續版本(Java5.0以及更高的版本)中,如果把resource宣告為volatile型別,那麼就能修正這個問題。

public class DoubleCheckedLocking {
    //對一個volatile變數的讀,總是能看到(任意執行緒)對這個volatile變數最後的寫入。
    private volatile static Resource resource;

    public static Resource getInstance() {
        if (resource == null) {
            synchronized (DoubleCheckedLocking.class) {
                if (resource == null)
                    resource = new Resource();
            }
        }
        return resource;
    }

    static class Resource {

    }
}

     缺乏同步會導致無法實現可見性,這使得確定何時寫入物件引用而不是原語值變得更加困難。在缺乏同步的情況下,可能會遇到某個物件引用的更新值(由另一個執行緒寫入)和該物件狀態的舊值同時存在。(這就是造成著名的雙重檢查鎖定(double-checked-locking)問題的根源,其中物件引用在沒有同步的情況下進行讀操作,產生的問題是您可能會看到一個更新的引用,但是仍然會通過該引用看到不完全構造的物件)。

     DCL方法已經被廣泛地廢棄了,延遲初始化佔位類模式能帶來同樣的優勢,並且更容易理解。

延遲初始化佔位

     使用一個專門的類來初始化Resource。JVM將推遲ResourceHolder的初始化操作,直到開始使用這個類時才初始化,並且由於通過一個靜態初始化來初始化Resource,因此不需要額外的同步。當任何一個執行緒第一次呼叫getResouce時,都會使ResouceHolder被載入和被初始化,此時靜態初始化器將執行Resource的初始化操作。

public class ResourceFactory {
     private static class ResourceHolder {
         public static Resource resource = new Resource();
     }

     public static Resource getResource() {
         return  ResourceHolder.resource ;
     }
}

Java併發程式設計實戰pdf及案例原始碼下載:
http://download.csdn.net/detail/xunzaosiyecao/9851028

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

相關文章