成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

java內(nèi)存模型

2i18ns / 2744人閱讀

摘要:順序一致性內(nèi)存模型有兩大特性一個(gè)線程中所有操作必須按照程序的順序執(zhí)行。這里的同步包括對(duì)常用同步原語(yǔ)的正確使用通過(guò)以下程序說(shuō)明與順序一致性兩種內(nèi)存模型的對(duì)比順序一致性模型中所有操作完全按程序的順序串行執(zhí)行。

java內(nèi)存模型 java內(nèi)存模型基礎(chǔ) happen-before模型

JSR-133使用happen-before的概念來(lái)闡述操作之間的內(nèi)存可見(jiàn)性。在JMM中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見(jiàn),那么這兩個(gè)操作之間必須要存在happen-before關(guān)系。在這里兩個(gè)操作可以在一個(gè)線程之內(nèi),也可以在不同的線程之間。與程序員相關(guān)的happen-before規(guī)則如下:

程序順序一致性:一個(gè)線程中的每個(gè)操作,happen-before于該線程中的任意后續(xù)操作。(不要扣字,若兩操作沒(méi)有依賴(lài)關(guān)系,且變更操作順序不影響結(jié)果,此時(shí)順序可以變更。與程序一致性規(guī)則不沖突)

監(jiān)視器鎖規(guī)則: 對(duì)一個(gè)鎖的解鎖,happen-before于隨后對(duì)這個(gè)鎖的加鎖。

volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫(xiě)操作,happen-before于任意后續(xù)對(duì)這個(gè)volatile域的讀。

傳遞性:如果A happen-before B且B happen-before C ,那么A happen-before C。

start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start(),那么A線程的ThreadB.start()操作happen-before于B中的任何操作。

join()規(guī)則:如果ThreadA 執(zhí)行操作ThreadB.join()并成功返回,那么ThreadB中的任意操作happen-before于ThreadA從ThreadB.join()操作成功返回。

兩個(gè)操作間具有happens-before關(guān)系,并不意味著前一個(gè)操作必須要在后一個(gè)操作之前執(zhí)行。happens-before僅僅要求前一個(gè)操作對(duì)后一個(gè)操作可見(jiàn)。

重排序

重排序是指編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段。重排序得遵循以下原則。

數(shù)據(jù)相互信賴(lài)的兩個(gè)操作不能進(jìn)行重排序

as-if-serial語(yǔ)言,不管怎么得排序(編譯器和處理器為了提高并行度),單線程程序執(zhí)行的結(jié)果不能改變。

重排序?qū)Χ嗑€程的影響,代碼如下:

/**
 * 操作1 操作2 之間無(wú)依賴(lài)關(guān)系, 可以進(jìn)行重排序
 * 操作3 操作4 之間無(wú)依賴(lài)關(guān)系, 可以進(jìn)行重排序
 * Thread B 中并不一定能看到Thread A 中對(duì)共享變量的寫(xiě)入。此時(shí)重排序操作破壞多線程語(yǔ)義
**/
class ReorderExample{
    int a = 0;
    boolean flag = false;
    public void writer(){                 //Thread A
        a = 1;                            //1
        flag = true;                      //2
    }
    public void reader(){                 //Thread B
        if(flag){                         //3
            int i = a * a;                //4
        }
    }
}
順序一致性

順序一致性內(nèi)存模型是一個(gè)理論參考模型,在設(shè)計(jì)的時(shí)候,處理器的內(nèi)存模型和編程語(yǔ)言的內(nèi)存模型都會(huì)以順序一致性內(nèi)存模型作為參照。順序一致性內(nèi)存模型有兩大特性:

一個(gè)線程中所有操作必須按照程序的順序執(zhí)行。

(不管程序是否同步)所有線程都只能看到單一的操作執(zhí)行順序,在順序一致性內(nèi)存模型中,每個(gè)操作都必須原子執(zhí)行且立即對(duì)所有線程可見(jiàn)。

JMM對(duì)正確同步的多線程程序的內(nèi)存一致性做了如下保證
如果程序是正確同步的,程序的執(zhí)行將具有順序一致性(Sequentially Consistent)--即程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中執(zhí)行結(jié)果相同。這里的同步包括對(duì)常用同步原語(yǔ)(Synchronized,volatile,final)的正確使用

通過(guò)以下程序說(shuō)明JMM與順序一致性 兩種內(nèi)存模型的對(duì)比

/**
 *順序一致性模型中,所有操作完全按程序的順序串行執(zhí)行。而在JMM中,臨界區(qū)內(nèi)的代碼
 *可以重排序(但JMM不允許臨界區(qū)內(nèi)的代碼“逸出”到臨界區(qū)之外,那樣會(huì)破壞監(jiān)視器的語(yǔ)
 *義)。JMM會(huì)在退出臨界區(qū)和進(jìn)入臨界區(qū)這兩個(gè)關(guān)鍵時(shí)間點(diǎn)做一些特別處理,使得線程在這兩
 *個(gè)時(shí)間點(diǎn)具有與順序一致性模型相同的內(nèi)存視圖,雖然線程A在臨界
 *區(qū)內(nèi)做了重排序,但由于監(jiān)視器互斥執(zhí)行的特性,這里的線程B根本無(wú)法“觀察”到線程A在臨
 *界區(qū)內(nèi)的重排序。這種重排序既提高了執(zhí)行效率,又沒(méi)有改變程序的執(zhí)行結(jié)果。
 *
 */
class SynchronizedExample {
    int a = 0;
    boolean flag = false;
    public synchronized void writer() {            // 獲取鎖
        a = 1;
        flag = true;
    }                                              // 釋放鎖
    public synchronized void reader() {            // 獲取鎖
        if (flag) {
        int i = a;
        ......
    }                                              // 釋放鎖
}

未同步程序在JMM中的執(zhí)行時(shí),整體上是無(wú)序的,其執(zhí)行結(jié)果無(wú)法預(yù)知。

順序一致性模型保證單線程內(nèi)的操作會(huì)按程序的順序執(zhí)行,而JMM不保證單線程內(nèi)的操作會(huì)按程序的順序執(zhí)行(比如上面正確同步的多線程程序在臨界區(qū)內(nèi)的重排序).

順序一致性模型保證所有線程只能看到一致的操作執(zhí)行順序,而JMM不保證所有線程能看到一致的操作執(zhí)行順序。

JMM不保證對(duì)64位的long型和double型變量的寫(xiě)操作具有原子性,而順序一致性模型保證對(duì)所有的內(nèi)存讀/寫(xiě)操作都具有原子性。

volatile內(nèi)存語(yǔ)義 volatile特性

可見(jiàn)性:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫(xiě)入。

原子性: 對(duì)任意單個(gè)volatile變量的讀/寫(xiě)具有原子性,但類(lèi)似于volatile++這種復(fù)合操作不具有原子性。

class VolatileFeaturesExample {
    volatile long vl = 0L;
    public void set(long l) {
        vl = l;
    }
    public void getAndIncrement () {   //復(fù)合volatile讀寫(xiě),不具有線程安全
        vl++;
    }
    public long get() {
        return vl;
    }
}
volatile 寫(xiě)-讀建立的happen-before關(guān)系

示例代碼如下:

根據(jù)程序次序規(guī)則,1 happen-before 2, 3 happen-before 4.(疑問(wèn)1:1與2,3與4兩操作沒(méi)有依賴(lài),為何不能重排,若發(fā)生重排,結(jié)果有可能會(huì)發(fā)生變化)

根據(jù)volatile規(guī)則 2 happen-before 3

根據(jù)happen-before傳遞性規(guī)則,1 happen-before 4.

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
        ......
    }
}
volatile 寫(xiě)-讀內(nèi)存原語(yǔ)

volatile寫(xiě)操作,JMM會(huì)把線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。

volatile讀操作,JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效。線程接下來(lái)將從主內(nèi)存中讀取共享變量。

volatile 內(nèi)存語(yǔ)義的實(shí)現(xiàn)

為了實(shí)現(xiàn)volatile內(nèi)存語(yǔ)義,JMM分分別限制這兩種重排序類(lèi)型,下圖JMM針對(duì)編譯器制定的volatile重排序規(guī)則表

這個(gè)重排序規(guī)則解釋了疑問(wèn)1。實(shí)現(xiàn):是通過(guò)編譯器生成字節(jié)碼時(shí),插入內(nèi)存屏障來(lái)達(dá)到這個(gè)限制,在此處不作展開(kāi),有興趣可以查閱相關(guān)資料

JSR-133增強(qiáng)volatile的內(nèi)存原語(yǔ)

在JSR-133之前的舊Java內(nèi)存模型中,雖然不允許volatile變量之間重排序,但舊的Java內(nèi)存模型允許volatile變量與普通變量重排序
因此,在舊的內(nèi)存模型中,volatile的寫(xiě)-讀沒(méi)有鎖的釋放-獲所具有的內(nèi)存語(yǔ)義。為了提供一種比鎖更輕量級(jí)的線程之間通信的機(jī)制,JSR-133專(zhuān)家組決定增強(qiáng)volatile的內(nèi)存語(yǔ)義:嚴(yán)格限制編譯器和處理器對(duì)volatile變量與普通變量的重排序,確保volatile的寫(xiě)-讀和鎖的釋放-獲取具有相同的內(nèi)存語(yǔ)義。從編譯器重排序規(guī)則和處理器內(nèi)存屏障插入策略來(lái)看,只要volatile變量與普通變量之間的重排序可能會(huì)破壞volatile的內(nèi)存語(yǔ)義,這種重排序就會(huì)被編譯器重排序規(guī)則和處理器內(nèi)存屏障插入策略禁止。

鎖的內(nèi)存語(yǔ)義 鎖的釋放與獲取內(nèi)存原語(yǔ)

當(dāng)線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中。
當(dāng)線程獲取鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效。從而使得被監(jiān)視器保護(hù)的臨界區(qū)代碼必須從主內(nèi)存中讀取共享變量。

/**
 *
 */
class MonitorExample {
    int a = 0;
    public synchronized void writer() {     // 1
        a++;          // 2
    }            // 3
    public synchronized void reader() {   // 4
        int i = a;        // 5
        ......
    }            // 6
}

假設(shè)線程A執(zhí)行writer()方法,隨后線程B執(zhí)行reader()方法。根據(jù)happens-before規(guī)則,這個(gè)過(guò)程包含的happens-before關(guān)系可以分為3類(lèi)。

根據(jù)程序次序規(guī)則,1 happens-before 2,2 happens-before 3;4 happens-before 5,5 happens-before 6。

根據(jù)監(jiān)視器鎖規(guī)則,3 happens-before 4。

根據(jù)happens-before的傳遞性,2 happens-before 5。

鎖的釋放與獲取的內(nèi)存語(yǔ)義

線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中

線程獲取鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效。從而使得被監(jiān)視器保護(hù)的臨界區(qū)代碼必須從主內(nèi)存中讀取共享變量

final 域內(nèi)存語(yǔ)義

final 域,編譯器與處理器要遵守兩個(gè)重排序規(guī)則

在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final域的寫(xiě)入,與隨后把這個(gè)被構(gòu)造對(duì)像的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序

初次讀一個(gè)包含final域的對(duì)象引用,與隨后初次讀這個(gè)final域,這兩個(gè)操作之間不能重排序(有點(diǎn)擾,eg:obj,obj.j的關(guān)系)

下面的示例代碼,說(shuō)明這兩個(gè)規(guī)則

/**
 * 
 */
public class FinalExample{
    int i;
    final int j;
    static FinalExample obj;
    static FinalExample(){
        i = 1;
        j = 2;
    }
    public static void writer(){
        obj =  new FinalExample();
    }
    public static void reader(){
        FinalExample object = obj;
        int a = obj.i;
        int b = obj.j;
    }
}
寫(xiě)final域重排序規(guī)則

MM禁止編譯器把final域的寫(xiě)重排序到構(gòu)造函數(shù)之外。

JMM編譯器會(huì)在final域的寫(xiě)之后,構(gòu)造函數(shù)return之前,插入一個(gè)StoreStore屏障。這個(gè)屏障禁止處理器把final域的寫(xiě)重排序到構(gòu)造函數(shù)之外

讀final域重排序規(guī)則

一個(gè)線程中,初次讀對(duì)象引用與初次讀該對(duì)象包含的final域,JMM禁止處理器重排序這兩個(gè)操作

分析上面代碼示例 reader()方法包含3個(gè)操作。

初次讀引用變量obj。

初次讀引用變量obj指向?qū)ο蟮钠胀ㄓ騤。

初次讀引用變量obj指向?qū)ο蟮膄inal域

final域?yàn)橐妙?lèi)型
/**
 *假設(shè)首先線程A執(zhí)行writeOne方法,執(zhí)行完后線程B執(zhí)行writetwo()方法,執(zhí)行完后線程執(zhí)行reader()方法
 *1是對(duì)final域的寫(xiě)入,2是對(duì)這個(gè)final域引用的對(duì)象的成員域的寫(xiě)入,3是把被構(gòu)造的對(duì)象的引用賦值給某個(gè)引用變量。這里除了前面提到的1不能和3重排序外,2和3也不能重排序
 */
public class FinalReferenceExample {
    final int[] intArray;
    static FinalReferenceExample obj;
    public FinalReferenceExample () {
        intArray = new int[1];                //1
        intArray[0] = 1;                      //2
    }
    public static void writerOne () {         //線程A
        obj = new FinalReferenceExample ();   //3
    }
    public static void writerTwo () {         //線程B 
        obj.intArray[0] = 2;                  //4
    }
    public static void reader () {            //線程C 
        if (obj != null) {                    //5
            int temp1 = obj.intArray[0];      //6
    }
}

本例final域?yàn)橐粋€(gè)引用類(lèi)型,它引用一個(gè)int型的數(shù)組對(duì)象。對(duì)于引用類(lèi)型,寫(xiě)final域的重排序規(guī)則對(duì)編譯器和處理器增加了如下約束:在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final引用的對(duì)象的成員域的寫(xiě)入,與隨后在構(gòu)造函數(shù)外把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序。

final引用不能從構(gòu)造函數(shù)內(nèi)溢出
/**
 * 步驟2使得構(gòu)造函數(shù)還未完成就對(duì)reader線程可見(jiàn)
 **/
public class FinalReferenceEscapeExample {
    final int i;
    static FinalReferenceEscapeExample obj;
    public FinalReferenceEscapeExample () {
        i = 1;                             // 1寫(xiě)final域
        obj = this;                        // 2 this引用在此"逸出"
    }
    public static void writer() {
        new FinalReferenceEscapeExample ();
    }
    public static void reader() {           
        if (obj != null) {                // 3
        int temp = obj.i;                 // 4
    }
}

結(jié)論:在構(gòu)造函數(shù)返回前,被構(gòu)造對(duì)象的引用不能為其他線程所見(jiàn)

雙重檢查鎖定與延遲初始化 java內(nèi)存模型綜述

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68857.html

相關(guān)文章

  • 來(lái),了解一下Java內(nèi)存模型(JMM)

    摘要:因?yàn)楣芾砣藛T是了解手下的人員以及自己負(fù)責(zé)的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線程場(chǎng)景下會(huì)存在緩存一致性問(wèn)題。有沒(méi)有發(fā)現(xiàn),緩存一致性問(wèn)題其實(shí)就是可見(jiàn)性問(wèn)題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機(jī)》和《Java并發(fā)編程的藝術(shù)》等書(shū)中也都有關(guān)于這個(gè)知識(shí)點(diǎn)的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說(shuō)自己更懵了。本文,就來(lái)整體的...

    kviccn 評(píng)論0 收藏0
  • 來(lái),了解一下Java內(nèi)存模型(JMM)

    摘要:因?yàn)楣芾砣藛T是了解手下的人員以及自己負(fù)責(zé)的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線程場(chǎng)景下會(huì)存在緩存一致性問(wèn)題。有沒(méi)有發(fā)現(xiàn),緩存一致性問(wèn)題其實(shí)就是可見(jiàn)性問(wèn)題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機(jī)》和《Java并發(fā)編程的藝術(shù)》等書(shū)中也都有關(guān)于這個(gè)知識(shí)點(diǎn)的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說(shuō)自己更懵了。本文,就來(lái)整體的...

    eccozhou 評(píng)論0 收藏0
  • 深入理解Java內(nèi)存模型(七)——總結(jié)

    摘要:編譯器,和處理器會(huì)共同確保單線程程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果相同。正確同步的多線程程序的執(zhí)行將具有順序一致性程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同。 前情提要 深入理解Java內(nèi)存模型(六)——final 處理器內(nèi)存模型 順序一致性內(nèi)存模型是一個(gè)理論參考模型,JMM和處理器內(nèi)存模型在設(shè)計(jì)時(shí)通常會(huì)把順序一致性內(nèi)存模型作為參照。JMM和處理器內(nèi)...

    paney129 評(píng)論0 收藏0
  • 簡(jiǎn)述Java內(nèi)存模型

    摘要:內(nèi)存模型即,簡(jiǎn)稱(chēng),其規(guī)范了虛擬機(jī)與計(jì)算機(jī)內(nèi)存時(shí)如何協(xié)同工作的,規(guī)定了一個(gè)線程如何和何時(shí)看到其他線程修改過(guò)的值,以及在必須時(shí),如何同步訪問(wèn)共享變量。內(nèi)存模型要求調(diào)用棧和本地變量存放在線程棧上,對(duì)象存放在堆上。 Java內(nèi)存模型即Java Memory Model,簡(jiǎn)稱(chēng)JMM,其規(guī)范了Java虛擬機(jī)與計(jì)算機(jī)內(nèi)存時(shí)如何協(xié)同工作的,規(guī)定了一個(gè)線程如何和何時(shí)看到其他線程修改過(guò)的值,以及在必須時(shí),...

    ACb0y 評(píng)論0 收藏0
  • JVM 探究(一):JVM內(nèi)存模型概念模型

    摘要:作為一個(gè)程序員,不了解內(nèi)存模型就不能寫(xiě)出能夠充分利用內(nèi)存的代碼。程序計(jì)數(shù)器是在電腦處理器中的一個(gè)寄存器,用來(lái)指示電腦下一步要運(yùn)行的指令序列。在虛擬機(jī)中,本地方法棧和虛擬機(jī)棧是共用同一塊內(nèi)存的,不做具體區(qū)分。 作為一個(gè) Java 程序員,不了解 Java 內(nèi)存模型就不能寫(xiě)出能夠充分利用內(nèi)存的代碼。本文通過(guò)對(duì) Java 內(nèi)存模型的介紹,讓讀者能夠了解 Java 的內(nèi)存的分配情況,適合 Ja...

    cnTomato 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

2i18ns

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<