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

溫馨提示×

溫馨提示×

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

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

Java單例一定要加volatile的原因是什么

發布時間:2022-05-30 11:12:25 來源:億速云 閱讀:177 作者:iii 欄目:開發技術

這篇文章主要介紹“Java單例一定要加volatile的原因是什么”,在日常操作中,相信很多人在Java單例一定要加volatile的原因是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java單例一定要加volatile的原因是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    前言:

    單例模式的實現方法有很多種,如餓漢模式、懶漢模式、靜態內部類和枚舉等,當面試官問到“為什么單例模式一定要加 volatile?”時,那么他指的是為什么懶漢模式中的私有變量要加 volatile?

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

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

    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 來保證程序的執行。那既然已經有 synchronized 來保證線程安全了,為什么還要給變量加 volatile 呢? 在解釋這個問題之前,我們先要搞懂一個前置知識:volatile 有什么用呢?

    1.volatile 作用

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

    1.1 內存可見性問題

    所謂內存可見性問題,指的是多個線程同時操作一個變量,其中某個線程修改了變量的值之后,其他線程感知不到變量的修改,這就是內存可見性問題。 而使用 volatile 就可以解決內存可見性問題,比如以下代碼,當沒有添加 volatile 時,它的實現如下:

    private static boolean flag = false;
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 如果 flag 變量為 true 就終止執行
                while (!flag) {
    
                }
                System.out.println("終止執行");
            }
        });
        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("設置 flag 變量的值為 true!");
                flag = true;
            }
        });
        t2.start();
    }

    以上程序的執行結果如下: 

    Java單例一定要加volatile的原因是什么

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

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

    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 就終止執行
                    while (!flag) {
    
                    }
                    System.out.println("終止執行");
                }
            });
            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("設置 flag 變量的值為 true!");
                    flag = true;
                }
            });
            t2.start();
        }
    }

    以上程序的執行結果如下: 

    Java單例一定要加volatile的原因是什么

     從上述執行結果我們可以看出,使用 volatile 之后就可以解決程序中的內存可見性問題了。

    1.2 防止指令重排序

    指令重排序是指在程序執行過程中,編譯器或 JVM 常常會對指令進行重新排序,已提高程序的執行性能。 指令重排序的設計初衷確實很好,在單線程中也能發揮很棒的作用,然而在多線程中,使用指令重排序就可能會導致線程安全問題了。

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

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

    2.為什么要用 volatile?

    回到主題,我們在單例模式中使用 volatile,主要是使用 volatile 可以禁止指令重排序,從而保證程序的正常運行。這里可能會有讀者提出疑問,不是已經使用了 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;
        }
    }

    注意觀察上述代碼,我標記了第 ① 處和第 ② 處的兩行代碼。給私有變量加 volatile 主要是為了防止第 ② 處執行時,也就是“instance = new Singleton()”執行時的指令重排序的,這行代碼看似只是一個創建對象的過程,然而它的實際執行卻分為以下 3 步:

    • 創建內存空間。

    • 在內存空間中初始化對象 Singleton。

    • 將內存地址賦值給 instance 對象(執行了此步驟,instance 就不等于 null 了)。

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

    到此,關于“Java單例一定要加volatile的原因是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

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

    AI

    枞阳县| 安塞县| 平南县| 镇巴县| 壶关县| 虹口区| 淅川县| 祁东县| 钟祥市| 青浦区| 南开区| 镇康县| 洞口县| 库伦旗| 扶风县| 延津县| 天台县| 田东县| 新闻| 延长县| 开鲁县| 通州区| 铅山县| 平陆县| 招远市| 徐州市| 乐平市| 抚州市| 哈尔滨市| 桂林市| 上饶市| 蓬莱市| 龙口市| 天气| 富平县| 老河口市| 柘荣县| 淳化县| 滨州市| 太原市| 惠来县|