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

Java中volatile?的作用

 更新時間:2022年05月10日 11:05:03   作者:??Java中文社群????  
這篇文章主要介紹了Java中volatile?的作用,volatile是Java并發(fā)編程的重要組成部分,主要作用是保證內(nèi)存的可見性和禁止指令重排序,下文更多對volatile作用的介紹,需要的小伙伴可以參考一下

前言:

volatile 是 Java 并發(fā)編程的重要組成部分,也是常見的面試題之一,它的主要作用有兩個:保證內(nèi)存的可見性和禁止指令重排序。下面我們具體來看這兩個功能。

內(nèi)存可見性

說到內(nèi)存可見性問題就不得不提 Java 內(nèi)存模型,Java 內(nèi)存模型(Java Memory Model)簡稱為 JMM,主要是用來屏蔽不同硬件和操作系統(tǒng)的內(nèi)存訪問差異的,因?yàn)樵诓煌挠布筒煌牟僮飨到y(tǒng)下,內(nèi)存的訪問是有一定的差異得,這種差異會導(dǎo)致相同的代碼在不同的硬件和不同的操作系統(tǒng)下有著不一樣的行為,而 Java 內(nèi)存模型就是解決這個差異,統(tǒng)一相同代碼在不同硬件和不同操作系統(tǒng)下的差異的。

Java 內(nèi)存模型規(guī)定:所有的變量(實(shí)例變量和靜態(tài)變量)都必須存儲在主內(nèi)存中,每個線程也會有自己的工作內(nèi)存,線程的工作內(nèi)存保存了該線程用到的變量和主內(nèi)存的副本拷貝,線程對變量的操作都在工作內(nèi)存中進(jìn)行。線程不能直接讀寫主內(nèi)存中的變量,

如下圖所示: 

 然而,Java 內(nèi)存模型會帶來一個新的問題,那就是內(nèi)存可見性問題,也就是當(dāng)某個線程修改了主內(nèi)存中共享變量的值之后,其他線程不能感知到此值被修改了,它會一直使用自己工作內(nèi)存中的“舊值”,這樣程序的執(zhí)行結(jié)果就不符合我們的預(yù)期了,這就是內(nèi)存可見性問題,我們用以下代碼來演示一下這個問題:

private static boolean flag = false;
public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            while (!flag) {

            }
            System.out.println("終止執(zhí)行");
        }
    });
    t1.start();
    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();
}

以上代碼我們預(yù)期的結(jié)果是,在線程 1 執(zhí)行了 1s 之后,線程 2 將 flag 變量修改為 true,之后線程 1 終止執(zhí)行,然而,因?yàn)榫€程 1 感知不到 flag 變量發(fā)生了修改,也就是內(nèi)存可見性問題,所以會導(dǎo)致線程 1 會永遠(yuǎn)的執(zhí)行下去,最終我們看到的結(jié)果是這樣的: 

 如何解決以上問題呢?只需要給變量 flag 加上 volatile 修飾即可,具體的實(shí)現(xiàn)代碼如下:

private volatile static boolean flag = false;
public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            while (!flag) {

            }
            System.out.println("終止執(zhí)行");
        }
    });
    t1.start();
    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é)果如下圖所示: 

禁止指令重排序

指令重排序是指編譯器或 CPU 為了優(yōu)化程序的執(zhí)行性能,而對指令進(jìn)行重新排序的一種手段。

指令重排序的實(shí)現(xiàn)初衷是好的,但是在多線程執(zhí)行中,如果執(zhí)行了指令重排序可能會導(dǎo)致程序執(zhí)行出錯。指令重排序最典型的一個問題就發(fā)生在單例模式中,

比如以下問題代碼:

public class Singleton {
    private Singleton() {}
    private static Singleton instance = null;
    public static Singleton getInstance() {
        if (instance == null) { // ①
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // ②
                }
            }
        }
        return instance;
    }
}

以上問題發(fā)生在代碼 ② 這一行“instance = new Singleton();”,這行代碼看似只是一個創(chuàng)建對象的過程,然而它的實(shí)際執(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 還未將對象實(shí)例化完,那么線程 2 將會得到一個被實(shí)例化“一半”的對象,從而導(dǎo)致程序執(zhí)行出錯,這就是為什么要給私有變量添加 volatile 的原因了。

要使以上單例模式變?yōu)榫€程安全的程序,需要給 instance 變量添加 volatile 修飾,它的最終實(shí)現(xiàn)代碼如下:

public class Singleton {
    private Singleton() {}
    // 使用 volatile 禁止指令重排序
    private static volatile Singleton instance = null; // 【主要是此行代碼發(fā)生了變化】
    public static Singleton getInstance() {
        if (instance == null) { // ①
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // ②
                }
            }
        }
        return instance;
    }
}

總結(jié)

volatile 是 Java 并發(fā)編程的重要組成部分,它的主要作用有兩個:保證內(nèi)存的可見性和禁止指令重排序。volatile 常使用在一寫多讀的場景中,比如 CopyOnWriteArrayList 集合,它在操作的時候會把全部數(shù)據(jù)復(fù)制出來對寫操作加鎖,修改完之后再使用 setArray 方法把此數(shù)組賦值為更新后的值,使用 volatile 可以使讀線程很快的告知到數(shù)組被修改,不會進(jìn)行指令重排,操作完成后就可以對其他線程可見了。

到此這篇關(guān)于Java中volatile 的作用的文章就介紹到這了,更多相關(guān)volatile的作用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC文件上傳中要解決的問題大匯總

    SpringMVC文件上傳中要解決的問題大匯總

    這篇文章主要介紹了SpringMVC文件上傳中要解決的問題,主要有中文文件名編碼問題,文件位置存儲問題以及文件名沖突問題等等,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • JAVA 實(shí)現(xiàn)磁盤文件加解密操作的示例代碼

    JAVA 實(shí)現(xiàn)磁盤文件加解密操作的示例代碼

    這篇文章主要介紹了JAVA 實(shí)現(xiàn)磁盤文件加解密操作的示例代碼,幫助大家利用Java實(shí)現(xiàn)文件的加解密,感興趣的朋友可以了解下
    2020-09-09
  • Mybatis中如何進(jìn)行批量更新(updateBatch)

    Mybatis中如何進(jìn)行批量更新(updateBatch)

    這篇文章主要介紹了Mybatis中如何進(jìn)行批量更新(updateBatch),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Java創(chuàng)建型設(shè)計(jì)模式之單例模式

    Java創(chuàng)建型設(shè)計(jì)模式之單例模式

    Java單例模式是一種設(shè)計(jì)模式,它確保一個類只有一個實(shí)例,并提供一個全局訪問點(diǎn)??梢允褂枚喾N方式實(shí)現(xiàn)單例模式,如餓漢式、懶漢式、雙重檢查鎖定、靜態(tài)內(nèi)部類、枚舉等,每種方式都有其優(yōu)缺點(diǎn),需要根據(jù)具體情況選擇使用
    2023-05-05
  • 最新評論

    桐乡市| 建瓯市| 天长市| 盈江县| 马关县| 改则县| 湟中县| 获嘉县| 方山县| 盐城市| 青铜峡市| 斗六市| 牟定县| 兴文县| 梅河口市| 大同市| 禹城市| 富蕴县| 汶上县| 弥勒县| 宁蒗| 木兰县| 新泰市| 海晏县| 塘沽区| 额济纳旗| 象州县| 神木县| 凌源市| 兴化市| 泗水县| 孟津县| 弥勒县| 兴仁县| 普定县| 英吉沙县| 台南县| 巴楚县| 申扎县| 广西| 当雄县|