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

并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語義

 更新時(shí)間:2021年11月04日 09:27:40   作者:李子捌  
這篇文章主要介紹了并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語義,理解volatile特性的一個(gè)好辦法是把對volatile變量的單個(gè)讀/寫,看成是使用同一個(gè)鎖對單個(gè)讀/寫操作做了同步。下面我們一起進(jìn)入文章看看具體例子吧,需要的小伙伴可以參考下

1、volatile的特性

理解volatile特性的一個(gè)好辦法是把對volatile變量的單個(gè)讀/寫,看成是使用同一個(gè)鎖對單個(gè)讀/寫操作做了同步。

代碼示例:

package com.lizba.p1;

/**
 * <p>
 *      volatile示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/9 21:34
 */
public class VolatileFeatureExample {

    /** 使用volatile聲明64位的long型變量 */
    volatile long v1 = 0l;

    /**
     * 單個(gè)volatile寫操作
     * @param l
     */
    public void set(long l) {
        v1 = l;
    }

    /**
     * 復(fù)合(多個(gè))volatile讀&寫
     */
    public void getAndIncrement() {
        v1++;
    }

    /**
     * 單個(gè)volatile變量的讀
     * @return
     */
    public long get() {
        return v1;
    }

}

假設(shè)有多個(gè)線程分別調(diào)用上面程序的3個(gè)方法,這個(gè)程序在語義上和下面程序等價(jià)。

package com.lizba.p1;

/**
 * <p>
 *      synchronized等價(jià)示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/9 21:46
 */
public class SynFeatureExample {

    /** 定義一個(gè)64位長度的普通變量 */
    long v1 = 0L;

    /**
     * 使用同步鎖對v1變量進(jìn)行寫操作
     * @param l
     */
    public synchronized void set(long l) {
        v1 = l;
    }


    /**
     *  通過同步讀和同步寫方法對v1進(jìn)行+1操作
     */
    public void getAndIncrement() {
        long temp = get();
        // v1加一
        temp += 1L;
        set(temp);
    }

    /**
     * 使用同步鎖對v1進(jìn)行讀操作
     * @return
     */
    public synchronized long get() {
        return v1;
    }

}

如上兩個(gè)程序所示,一個(gè)volatile變量的單個(gè)讀\寫操作,與一個(gè)普通變量的讀\寫操作都是使用同一個(gè)鎖來同步,它們之間的執(zhí)行效果相同。

上述代碼總結(jié):

鎖的happens-before規(guī)則保證釋放鎖和獲取鎖的兩個(gè)線程之間的內(nèi)存可見性,這意味著對一個(gè)volatile變量的讀,總能看到(任意線程)對這個(gè)volatile變量最后的寫入。
鎖的語義決定了臨界區(qū)代碼的執(zhí)行具有原子性。這意味著,即使是64位的long型和double型變量,只要它是volatile變量,對該變量的讀/寫就具有原子性。如果是多個(gè)volatile操作或類似于volatile++這種復(fù)合操作,這些操作整體上不具備原子性。

總結(jié)volatile特性:

  • 可見性。對一個(gè)volatile變量的讀,總是能看到(任意線程)對這個(gè)volatile變量最后的寫入。
  • 原子性。對任意volatile變量的讀/寫具有原子性,但類似volatile++這種復(fù)合操作不具有原子性。

2、volatile寫-讀建立的happens-before關(guān)系

  • 對于程序員來說,我們更加需要關(guān)注的是volatile對線程內(nèi)存的可見性。

從JDK1.5(JSR-133)開始,volatile變量的寫-讀可以實(shí)現(xiàn)線程之間的通信。從內(nèi)存語義的角度來說,volatile的寫-讀與鎖的釋放-獲取有相同的內(nèi)存效果。

  • volatile的寫和鎖的釋放有相同的內(nèi)存語義
  • volatile的讀和鎖的獲取有相同的內(nèi)存語義

代碼示例:

package com.lizba.p1;

/**
 * <p>
 *
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/9 22:23
 */
public class VolatileExample {

    int a = 0;

    volatile boolean flag = false;

    public void writer() {
        a = 1;                              // 1
        flag = true;                        // 2
    }

    public void reader() {
        if (flag) {                         // 3
            int i = a;                      // 4
            System.out.println(i);
        }
    }
    
}

假設(shè)線程A執(zhí)行writer()方法之后,線程B執(zhí)行reader()方法。根據(jù)happens-before規(guī)則,

這個(gè)過程建立的happens-before關(guān)系如下:

  • 根據(jù)程序次序規(guī)則,1 happens-before 2, 3 happens-before 4
  • 根據(jù)volatile規(guī)則,2 happens-before 3
  • 根據(jù)happens-before的傳遞性規(guī)則,1 happens-before 4。

圖示上述happens-before關(guān)系:

總結(jié):這里A線程寫一個(gè)volatile變量后,B線程讀同一個(gè)volatile變量。A線程在寫volatile變量之前所有可見的共享變量,在B線程讀同一個(gè)volatile變量后,將立即對B線程可見。

3、volatile寫-讀的內(nèi)存語義

volatile寫的內(nèi)存語義

當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。

以上面的VolatileExample為例,假設(shè)A線程首先執(zhí)行writer()方法,隨后線程B執(zhí)行reader()方法,初始時(shí)兩個(gè)線程的本地內(nèi)存中的flag和a都是初始狀態(tài)。

A執(zhí)行volatile寫后,共享變量狀態(tài)示意圖。

線程A在寫flag變量后,本地內(nèi)存A中被線程A更新過的兩個(gè)共享變量的值被刷新到主內(nèi)存中,此時(shí)A的本地內(nèi)存和主內(nèi)存中的值是一致的。

volatile讀的內(nèi)存語義

當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對應(yīng)的本地內(nèi)存置為無效。線程接下來將會(huì)從主內(nèi)存中讀取共享變量。

B執(zhí)行volatile讀后,共享變量的狀態(tài)示意圖:

在讀flag變量后,本地內(nèi)存B包含的值已經(jīng)被置為無效。此時(shí),線程B必須從主內(nèi)存中重新讀取共享變量。線程B的讀取操作將導(dǎo)致本地內(nèi)存B與主內(nèi)存中的共享變量的值變?yōu)橐恢隆?/p>

總結(jié)volatile的寫和volatile讀的內(nèi)存語義

  • 線程A寫一個(gè)volatile變量,實(shí)質(zhì)上是線程A向接下來將要讀這個(gè)volatile變量的某個(gè)線程發(fā)出了(其對共享變量所做修改的)消息。
  • 線程B讀一個(gè)volatile變量,實(shí)質(zhì)上是線程B接收了之前某個(gè)線程發(fā)出的(在寫這個(gè)volatile變量之前對共享變量所做修改的)消息。
  • 線程A寫一個(gè)volatile變量,隨后線程B讀這個(gè)volatile變量,這個(gè)過程實(shí)質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息。

4、volatile內(nèi)存語義實(shí)現(xiàn)

程序的重排序分為編譯器重排序和處理器重排序(我的前面的博文內(nèi)容有寫哈)。為了實(shí)現(xiàn)volatile內(nèi)存語義,JMM會(huì)分別禁止這兩種類型的重排序。

volatile重排序規(guī)則表:

是否能重排序 第二個(gè)操作
第一個(gè)操作 普通讀/寫 volatile讀 volatile寫
普通讀/寫 NO
volatile讀 NO NO NO
volatile寫 NO NO

上圖舉例:第一行最后一個(gè)單元格意思是,在程序中第一個(gè)操作為普通讀/寫時(shí),如果第二個(gè)操作為volatile寫,則編譯器不能重排序。

總結(jié)上圖:

  • 第二個(gè)操作是volatile寫時(shí),都不能重排序。確保volatile寫之前的操作不會(huì)被編譯器重排序到volatile之后
  • 第一個(gè)操作為volatile讀時(shí),都不能重排序。確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile之前
  • 第一個(gè)操作為volatile寫,第二個(gè)操作為volatile讀時(shí),不能重排序。

為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。

JMM采取的是保守策略內(nèi)存屏障插入策略,如下:

  • 在每個(gè)volatile寫操作屏障前面插入一個(gè)StoreStore屏障。
  • 在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障
  • 在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。
  • 在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障。

保守策略可以保證在任意處理器平臺上,任意程序中都能得到正確的volatile內(nèi)存語義。

保守策略下,volatile寫插入內(nèi)存屏障后生成的指令序列圖:

解釋:

StoreStore屏障可以保證在volatile寫之前,其前面所有普通寫操作已經(jīng)對任意處理器可見了。這是因?yàn)镾toreStore屏障將保障上面所有普通寫在volatile寫之前刷新到主內(nèi)存。

保守策略下,volatile讀插入內(nèi)存屏障后生成的指令序列圖:

解釋:

LoadLoad屏障用來禁止處理器把上面的volatile讀與下面的普通讀重排序。LoadStore屏障用來禁止處理器把上面的volatile讀與下面的普通寫重排序。

上述volatile寫和volatile讀的內(nèi)存屏障插入策略非常保守。在實(shí)際執(zhí)行時(shí),只要不改變volatile寫-讀的內(nèi)存語義,編譯器可以根據(jù)具體情況省略不必要的屏障。

代碼示例:

package com.lizba.p1;

/**
 * <p>
 *      volatile屏障示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/9 23:48
 */
public class VolatileBarrierExample {

    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    void readAndWrite() {
        // 第一個(gè)volatile讀
        int i = v1;
        // 第二個(gè)volatile讀
        int j = v2;
        // 普通寫
        a = i + j;
        // 第一個(gè)volatile寫
        v1 = i + 1;
        // 第二個(gè)volatile寫
        v2 = j * 2;
    }

    // ... 其他方法

}

針對VolatileBarrierExample的readAndWrite(),編譯器生成字節(jié)碼時(shí)可以做如下優(yōu)化:

注意:最后的StoreLoad屏障無法省略。因?yàn)榈诙€(gè)volatile寫之后,程序return。此時(shí)編譯器無法準(zhǔn)確斷定后面是否會(huì)有volatile讀寫操作,為了安全起見,編譯器通常會(huì)在這里插入一個(gè)StoreLoad屏障。

上面的優(yōu)化可以針對任意處理器平臺,但是由于不同的處理器有不同的“松緊度”的處理器內(nèi)存模型,內(nèi)存屏障的插入還可以根據(jù)具體的處理器內(nèi)存模型繼續(xù)優(yōu)化。

X86處理器平臺優(yōu)化

X86處理器僅會(huì)對寫-讀操作做重排序。X86不會(huì)對讀-讀、讀-寫和寫-寫重排序,因此X86處理器會(huì)省略掉這3種操作類型對應(yīng)的內(nèi)存屏障。在X86平臺中,JMM僅需要在volatile寫后插入一個(gè)StoreLoad屏障即可正確實(shí)現(xiàn)volatile寫-讀內(nèi)存語義。同時(shí)這樣意味著X86處理器中,volatile寫的開銷會(huì)遠(yuǎn)遠(yuǎn)大于讀的開銷。

5、volatile和鎖的比較

功能上:

鎖比volatile更強(qiáng)大

可伸縮性和執(zhí)行性能上:

volatile更具有優(yōu)勢

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

相關(guān)文章

  • mybatis-plus的添加與修改詳解

    mybatis-plus的添加與修改詳解

    這篇文章主要介紹了mybatis-plus的添加與修改方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • elasticsearch kibana簡單查詢講解

    elasticsearch kibana簡單查詢講解

    今天小編就為大家分享一篇關(guān)于elasticsearch kibana簡單查詢講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • Java通過經(jīng)緯度坐標(biāo)獲取兩個(gè)點(diǎn)之間的直線距離的示例

    Java通過經(jīng)緯度坐標(biāo)獲取兩個(gè)點(diǎn)之間的直線距離的示例

    這篇文章主要介紹了Java通過經(jīng)緯度坐標(biāo)獲取兩個(gè)點(diǎn)之間的直線距離的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java實(shí)現(xiàn)俄羅斯方塊游戲簡單版

    Java實(shí)現(xiàn)俄羅斯方塊游戲簡單版

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)俄羅斯方塊游戲簡單版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • mybaits中if條件中怎樣判斷布爾值

    mybaits中if條件中怎樣判斷布爾值

    這篇文章主要介紹了mybaits中if條件中怎樣判斷布爾值問題,具有很好的參考價(jià)值,希望對大家有所幫助,
    2023-08-08
  • WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法

    WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法

    這篇文章主要介紹了WebDriver中實(shí)現(xiàn)對特定的Web區(qū)域截圖方法,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-06-06
  • Java servlet執(zhí)行流程代碼實(shí)例

    Java servlet執(zhí)行流程代碼實(shí)例

    這篇文章主要介紹了Java servlet執(zhí)行流程代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Spring?WebFlux怎么進(jìn)行異常處理源碼解析

    Spring?WebFlux怎么進(jìn)行異常處理源碼解析

    這篇文章主要為大家介紹了Spring?WebFlux怎么進(jìn)行異常處理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • HandlerMapping之RequestMappingHandlerMapping作用詳解

    HandlerMapping之RequestMappingHandlerMapping作用詳解

    這篇文章主要介紹了HandlerMapping之RequestMappingHandlerMapping作用詳解,HandlerMapping是用來尋找Handler的,并不與Handler的類型或者實(shí)現(xiàn)綁定,而是根據(jù)需要定義的,那么為什么要單獨(dú)給@RequestMapping實(shí)現(xiàn)一個(gè)HandlerMapping,需要的朋友可以參考下
    2023-10-10
  • SpringBoot如何實(shí)現(xiàn)各種參數(shù)校驗(yàn)

    SpringBoot如何實(shí)現(xiàn)各種參數(shù)校驗(yàn)

    文章詳細(xì)介紹了SpringValidation的使用,包括簡單使用、requestBody參數(shù)校驗(yàn)、requestParam/PathVariable參數(shù)校驗(yàn)、統(tǒng)一異常處理、分組校驗(yàn)、嵌套校驗(yàn)、集合校驗(yàn)、自定義校驗(yàn)、編程式校驗(yàn)、快速失敗、@Valid和@Validated的區(qū)別以及實(shí)現(xiàn)原理
    2024-12-12

最新評論

岳阳县| 新田县| 怀远县| 望城县| 休宁县| 隆林| 章丘市| 尚志市| 城固县| 江口县| 宝应县| 稻城县| 广宗县| 和林格尔县| 巴林左旗| 思南县| 大英县| 尼木县| 乌海市| 昌图县| 无为县| 揭西县| 安陆市| 方城县| 修武县| 青阳县| 荥阳市| 西林县| 罗甸县| 英德市| 泗洪县| 左权县| 温宿县| 临夏市| 平安县| 花莲县| 沽源县| 麻江县| 瑞金市| 中西区| 富顺县|