亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

雙重檢查鎖為什么要使用volatile字段?

發布時間:2020-08-11 20:30:50 來源:網絡 閱讀:352 作者:Java_老男孩 欄目:編程語言

雙重鎖的由來

單例模式中,有一個DCL(雙重鎖)的實現方式。在Java程序中,有時候可能需要推遲一些高開銷的對象初始化操作,并且只有在使用這些對象時才開始初始化。

下面是非線程安全的延遲初始化對象的實例代碼。

/**
 * @author xiaoshu
 */
public class Instance {
}

/**
 * 非線程安全的延遲初始化對象
 *
 * @author xiaoshu
 */
public class UnsafeLazyInitialization {
    private static Instance instance;

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

在UnsafeLazyInitialization類中,假設A線程執行代碼1的同時,B線程執行代碼2。此時,線程A可能會看到instance引用對象還沒有完成初始化。

對于UnsafeLazyInitialization類,我們可以對getInstance()方法做同步處理來實現線程安全的延遲初始化。示例代碼如下。

/**
 * 安全的延遲初始化
 *
 * @author xiaoshu
 */
public class SafeLazyInitialization {
    private static Instance instance;

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

由于對getInstance()方法做了同步處理,synchronized將導致性能開銷。如果getInstance()方法被多個線程頻繁的調用,將會導致程序執行性能的下降。反之,如果getInstance()方法不會被多個線程頻繁的調用,那么這個延遲初始化方案將能提供令人滿意的性能。

后來,提出了一個“聰明”的技巧:雙重檢查鎖定(Double-Checked Locking)。想通過雙重檢查鎖定來降低同步的開銷。下面是使用雙重檢查鎖定來實現延遲初始化的實例代碼。

/**
 * 雙重檢查鎖定
 *
 * @author xiaoshu
 */
public class DoubleCheckedLocking {
    private static Instance instance;

    public static Instance getInstance() {
        if (null == instance) {                             //1.第一次檢查
            synchronized (DoubleCheckedLocking.class) {     //2.加鎖
                if (null == instance) {                     //3:第二次檢查
                    instance = new Instance();              //4.問題的根源出在這里
                }
            }
        }
        return instance;
    }
}

雙重檢查鎖定看起來似乎很完美,但這是一個錯誤的優化!在線程執行到第1處,代碼讀取到instance不為null時,instance引用的對象有可能還沒有完成初始化。

問題的根源

前面的雙重檢查鎖定實例代碼的第4處(instance = new Instance();)創建了一個對象。這一行代碼可以分解為如下的3行偽代碼。

memory = allocate();    //1.分配對象的內存空間
ctorInstance(memory); //2.初始化對象
instance = memory;        //3.設置instance指向剛分配的內存地址

上面3行偽代碼中的2和3之間,可能會被重排序(在一些JIT編譯器上,這種重排序是真實發生的),2和3之間重排序之后的執行時序如下:

memory = allocate();    //1.分配對象的內存空間
instance = memory;        //3.設置instance指向剛分配的內存地址
                                            //注意,此時對象還沒有被初始化!
ctorInstance(memory); //2.初始化對象

多線程執行時序表

時間 線程A 線程B
T1 A1:分配對象的內存空間
T2 A3:設置instance指向內存空間
T3 B1:判斷instance是否為空
T4 B2:由于instance不為null,線程B將訪問instance引用的對象
T5 A2:初始化對象
T6 A4:訪問instance引用的對象

在知曉了問題發生的根源之后,我們可以想出兩個方法來實現線程安全的延遲初始化。

1)不允許2和3重排序

2)允許2和3重排序,但不允許其他線程“看到”這個重排序。

后文介紹的兩個解決方案,分別對應于上面這兩點。

解決方案一:基于volatile的解決方案

/**
 * 安全的雙重檢查鎖定
 *
 * @author xiaoshu
 */
public class SafeDoubleCheckedLocking {
    private volatile static Instance instance;

    public static Instance getInstance() {
        if (null == instance) {
            synchronized (SafeDoubleCheckedLocking.class) {
                if (null == instance) {
                    instance = new Instance();//instance為volatile,現在沒有問題了。
                }
            }
        }
        return instance;
    }
}

注意:這個解決方案需要JDK5或更高版本(因為從JDK5開始使用新的JSR-133內存模型規范,這個規范增強了volatile的語義)。

當聲明對象的引用為volatile后,3行偽代碼中的2和3之間的重排序,在多線程環境中將會被禁止。

解決方案二:基于類初始化的解決方案

JVM在類的初始化階段(即在Class被加載后,且被線程使用之前),會執行類的初始化。在執行類的初始化期間,JVM會去獲取一個鎖.這個鎖可以同步多個線程對同一個類的初始化。

基于這個特性,可以實現另一種線程安全的延遲初始化方案(這個方案被稱之為Initialization On Demand Holder idiom)。

/**
 * 基于類初始化的解決方案
 *
 * @author xiaoshu
 */
public class InstanceFactory {
    private static class InstanceHolder {
        private static Instance instance = new Instance();
    }

    public static Instance getInstance() {
        return InstanceHolder.instance; //這里將導致InstanceHolder類被初始化
    }
}

字段延遲初始化降低了初始化類或創建實例的開銷,但增加了訪問被延遲初始化的字段的開銷。在大多數時候,正常的初始化要優于延遲初始化。如果確實需要對實例字段使用線程安全的延遲初始化,請使用上面介紹的基于volatile的延遲初始化的方案;如果確實需要對靜態字段使用線程安全的延遲初始化,請使用上面介紹的基于類初始化的方案。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

竹北市| 平昌县| 萍乡市| 壤塘县| 瓮安县| 米泉市| 渝中区| 维西| 宁南县| 长汀县| 弥渡县| 来安县| 金秀| 上饶县| 邛崃市| 滦南县| 莱芜市| 大荔县| 大港区| 商丘市| 宜章县| 兰州市| 如东县| 濉溪县| 瓮安县| 酒泉市| 简阳市| 江油市| 新宁县| 罗山县| 涡阳县| 洞头县| 德钦县| 鸡西市| 河津市| 墨竹工卡县| 乌鲁木齐县| 都兰县| 淄博市| 隆林| 德兴市|