最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java開發(fā)中為什么雙重效驗鎖要加volatile

 更新時間:2023年06月01日 11:21:21   作者:javacn_site  
這篇文章主要為大家介紹了java開發(fā)中為什么雙重效驗鎖要加volatile原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

單例模式的實現(xiàn)方法有很多種,如餓漢模式、懶漢模式、靜態(tài)內(nèi)部類和枚舉等,當(dāng)面試官問到“為什么雙重效驗鎖要加volatile?”時,那么他指的是為什么懶漢模式中的私有變量要加 volatile?

懶漢模式指的是對象的創(chuàng)建是懶加載的方式,并不是在程序啟動時就創(chuàng)建對象,而是第一次被真正使用時才創(chuàng)建對象。

要解釋為什么要加 volatile?我們先來看懶漢模式的具體實現(xiàn)代碼:

public class Singleton {
    // 1.防止外部直接 new 對象破壞單例模式
    private Singleton() {}
    // 2.通過私有變量保存單例對象【添加了 volatile 修飾】
    private static volatile Singleton instance = null;
    // 3.提供公共獲取單例對象的方法
    public static Singleton getInstance() {
        if (instance == null) { // 第 1 次效驗
            synchronized (Singleton.class) {
                if (instance == null) { // 第 2 次效驗
                    instance = new Singleton(); 
                }
            }
        }
        return instance;
    }
}

從上述代碼可以看出,為了保證線程安全和高性能,代碼中使用了兩次 if 和 synchronized 來保證程序的執(zhí)行。那既然已經(jīng)有 synchronized 來保證線程安全了,為什么還要給變量加 volatile 呢?

在解釋這個問題之前,我們先要搞懂一個前置知識:volatile 有什么用呢?

1.volatile 作用

volatile 有兩個主要的作用,第一,解決內(nèi)存可見性問題,第二,防止指令重排序。

1.1 內(nèi)存可見性問題

所謂內(nèi)存可見性問題,指的是多個線程同時操作一個變量,其中某個線程修改了變量的值之后,其他線程感知不到變量的修改,這就是內(nèi)存可見性問題。

而使用 volatile 就可以解決內(nèi)存可見性問題,比如以下代碼,當(dāng)沒有添加 volatile 時,它的實現(xiàn)如下:

private static boolean flag = false;
public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            // 如果 flag 變量為 true 就終止執(zhí)行
            while (!flag) {
            }
            System.out.println("終止執(zhí)行");
        }
    });
    t1.start();
    // 1s 之后將 flag 變量的值修改為 true
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("設(shè)置 flag 變量的值為 true!");
            flag = true;
        }
    });
    t2.start();
}

以上程序的執(zhí)行結(jié)果如下:

然而,以上程序執(zhí)行了 N 久之后,依然沒有結(jié)束執(zhí)行,這說明線程 2 在修改了 flag 變量之后,線程 1 根本沒有感知到變量的修改。

那么接下來,我們嘗試給 flag 加上 volatile,實現(xiàn)代碼如下:

public class volatileTest {
    private static volatile boolean flag = false;
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 如果 flag 變量為 true 就終止執(zhí)行
                while (!flag) {
                }
                System.out.println("終止執(zhí)行");
            }
        });
        t1.start();
        // 1s 之后將 flag 變量的值修改為 true
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("設(shè)置 flag 變量的值為 true!");
                flag = true;
            }
        });
        t2.start();
    }
}

以上程序的執(zhí)行結(jié)果如下:

從上述執(zhí)行結(jié)果我們可以看出,使用 volatile 之后就可以解決程序中的內(nèi)存可見性問題了。

1.2 防止指令重排序

指令重排序是指在程序執(zhí)行過程中,編譯器或 JVM 常常會對指令進行重新排序,已提高程序的執(zhí)行性能。

指令重排序的設(shè)計初衷確實很好,在單線程中也能發(fā)揮很棒的作用,然而在多線程中,使用指令重排序就可能會導(dǎo)致線程安全問題了。

所謂線程安全問題是指程序的執(zhí)行結(jié)果,和我們的預(yù)期不相符。比如我們預(yù)期的正確結(jié)果是 0,但程序的執(zhí)行結(jié)果卻是 1,那么這就是線程安全問題。

而使用 volatile 可以禁止指令重排序,從而保證程序在多線程運行時能夠正確執(zhí)行。

2.為什么要用 volatile?

回到主題,我們在單例模式中使用 volatile,主要是使用 volatile 可以禁止指令重排序,從而保證程序的正常運行。這里可能會有讀者提出疑問,不是已經(jīng)使用了 synchronized 來保證線程安全嗎?那為什么還要再加 volatile 呢?看下面的代碼:

public class Singleton {
    private Singleton() {}
    // 使用 volatile 禁止指令重排序
    private static volatile Singleton instance = null;
    public static Singleton getInstance() {
        if (instance == null) { // ①
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // ②
                }
            }
        }
        return instance;
    }
}

注意觀察上述代碼,我標(biāo)記了第 ① 處和第 ② 處的兩行代碼。給私有變量加 volatile 主要是為了防止第 ② 處執(zhí)行時,也就是“instance = new Singleton()”執(zhí)行時的指令重排序的,這行代碼看似只是一個創(chuàng)建對象的過程

然而它的實際執(zhí)行卻分為以下 3 步:

  • 創(chuàng)建內(nèi)存空間。
  • 在內(nèi)存空間中初始化對象 Singleton。
  • 將內(nèi)存地址賦值給 instance 對象(執(zhí)行了此步驟,instance 就不等于 null 了)。

試想一下,如果不加 volatile,那么線程 1 在執(zhí)行到上述代碼的第 ② 處時就可能會執(zhí)行指令重排序,將原本是 1、2、3 的執(zhí)行順序,重排為 1、3、2。但是特殊情況下,線程 1 在執(zhí)行完第 3 步之后,如果來了線程 2 執(zhí)行到上述代碼的第 ① 處,判斷 instance 對象已經(jīng)不為 null,但此時線程 1 還未將對象實例化完,那么線程 2 將會得到一個被實例化“一半”的對象,從而導(dǎo)致程序執(zhí)行出錯,這就是為什么要給私有變量添加 volatile 的原因了。

小結(jié)

使用 volatile 可以解決內(nèi)存可見性問題和防止指令重排序,我們在單例模式中使用 volatile 主要是使用 volatile 的后一個特性(防止指令重排序),從而避免多線程執(zhí)行的情況下,因為指令重排序而導(dǎo)致某些線程得到一個未被完全實例化的對象,從而導(dǎo)致程序執(zhí)行出錯的情況。

以上就是java開發(fā)中為什么雙重效驗鎖要加volatile的詳細(xì)內(nèi)容,更多關(guān)于java雙重效驗鎖volatile的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • hadoop 單機安裝配置教程

    hadoop 單機安裝配置教程

    單機安裝主要用于程序邏輯調(diào)試。安裝步驟基本通分布式安裝,包括環(huán)境變量,主要Hadoop配置文件,SSH配置等,需要的朋友可以參考下
    2012-11-11
  • SpringBoot去除參數(shù)前后空格和XSS過濾

    SpringBoot去除參數(shù)前后空格和XSS過濾

    本文主要介紹了SpringBoot去除參數(shù)前后空格和XSS過濾,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Springboot?maven項目配置文件覆蓋問題的處理

    Springboot?maven項目配置文件覆蓋問題的處理

    這篇文章主要介紹了Springboot?maven項目配置文件覆蓋問題的處理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • IDEA2020.1構(gòu)建Spring5.2.x源碼的方法

    IDEA2020.1構(gòu)建Spring5.2.x源碼的方法

    這篇文章主要介紹了IDEA2020.1構(gòu)建Spring5.2.x源碼的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • SpringBoot下如何實現(xiàn)支付寶接口的使用

    SpringBoot下如何實現(xiàn)支付寶接口的使用

    這篇文章主要介紹了SpringBoot下如何實現(xiàn)支付寶接口的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Java實現(xiàn)Word轉(zhuǎn)PDF的兩種常見方案和性能實測

    Java實現(xiàn)Word轉(zhuǎn)PDF的兩種常見方案和性能實測

    這篇文章對比了Java后端開發(fā)中Word轉(zhuǎn)PDF的兩種常見方案,即docx4j(純Java實現(xiàn))和LibreOffice(調(diào)用Office原生引擎),下面小編就和大家詳細(xì)介紹一下具體實現(xiàn)方法吧
    2026-01-01
  • Java中常見的陷阱題及答案

    Java中常見的陷阱題及答案

    在電腦里找到一份當(dāng)時學(xué)習(xí)JAVA時的筆記,看到一些現(xiàn)在已經(jīng)遺忘的細(xì)節(jié)。稍微整理了幾個,發(fā)出來與大家分享。這篇文章主要介紹了Java中常見的陷阱題及答案,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口的處理方法

    idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口的處理方法

    這篇文章主要介紹了idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 又一波Java專業(yè)人士必備書籍來襲

    又一波Java專業(yè)人士必備書籍來襲

    又一波Java專業(yè)人士必備書籍來襲,這篇文章主要向大家推薦了Java專業(yè)人士必讀的書,感興趣的小伙伴們不要錯過
    2016-09-09
  • 使用java.util.Timer實現(xiàn)任務(wù)調(diào)度

    使用java.util.Timer實現(xiàn)任務(wù)調(diào)度

    這篇文章主要為大家詳細(xì)介紹了使用java.util.Timer實現(xiàn)任務(wù)調(diào)度,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03

最新評論

资溪县| 潜江市| 南皮县| 六枝特区| 中超| 丰县| 广州市| 汽车| 即墨市| 区。| 天镇县| 汪清县| 汤阴县| 皋兰县| 定州市| 宁河县| 汽车| 方山县| 文水县| 太仆寺旗| 洪泽县| 日照市| 海宁市| 建瓯市| 辽阳县| 阿拉尔市| 依兰县| 昭苏县| 临汾市| 蓝田县| 汽车| 留坝县| 盈江县| 南丹县| 漳浦县| 和林格尔县| 毕节市| 泾源县| 随州市| 拜城县| 韶山市|