Java內(nèi)存模型

Java Memory Model,通過定義程序中各個變量的訪問規(guī)則帆调,即在虛擬機(jī)中將變量存儲到內(nèi)存和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)砚殿,屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異。

1.JMM的抽象結(jié)構(gòu)

如下圖所示先朦,線程之間的共享變量(實例域,靜態(tài)域和數(shù)組元素)存儲在主內(nèi)存(Main Memory)中犬缨,每個線程都有一個私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本喳魏。JMM決定一個線程對共享變量的寫入何時對另一個線程可見。

在上圖中怀薛,如果線程A和線程B要進(jìn)行通信刺彩,必須要經(jīng)歷以下兩個步驟:

  1. 線程A把本地內(nèi)存中A中更新近的共享變量刷新到主內(nèi)存中去。
  2. 線程B到主內(nèi)存中去讀取線程A之前已更新過的共享變量枝恋。

2.內(nèi)存間的交互操作

關(guān)于主內(nèi)存與工作內(nèi)存之間具體的交互協(xié)議创倔,即一個變量如何從主內(nèi)存拷貝到工作內(nèi)存、如何從工作內(nèi)存同步回主內(nèi)存之類的實現(xiàn)細(xì)節(jié)焚碌,Java內(nèi)存模型中定義了以下8種操作來完成畦攘,虛擬機(jī)實現(xiàn)時必須保證下面提及的每一種操作都是原子的、不可再分的十电。

  • lock(鎖定):作用于主內(nèi)存的變量知押,它把一個變量標(biāo)識為一條線程獨(dú)占的狀態(tài)。
  • unlock(解鎖):作用于主內(nèi)存的變量鹃骂,它把一個處于鎖定狀態(tài)的變量釋放出來台盯,釋放后的變量才可以被其他線程鎖定。
  • read(讀任废摺):作用于主內(nèi)存的變量静盅,它把一個變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動作使用寝殴。
  • load(載入):作用于工作內(nèi)存的變量蒿叠,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中明垢。
  • use(使用):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個變量的值傳遞給執(zhí)行引擎栈虚,每當(dāng)虛擬機(jī)遇到一個需要使用到變量的值的字節(jié)碼指令時將會執(zhí)行這個操作袖外。
  • assign(賦值):作用于工作內(nèi)存的變量,它把一個從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量魂务,每當(dāng)虛擬機(jī)遇到一個給變量賦值的字節(jié)碼指令時執(zhí)行這個操作曼验。
  • store(存儲):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個變量的值傳送到主內(nèi)存中粘姜,以便隨后的write操作使用鬓照。
  • write(寫入):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中孤紧。

3.重排序

重排序是指編繹器處理器為了優(yōu)化程序性能而對指令序列進(jìn)行重新排序的一種手段豺裆。重排序分為編譯器優(yōu)化的重排序,指令級并行的重排序和內(nèi)存系統(tǒng)的重排序号显。從Java源代碼到最終實際執(zhí)行的指令序列臭猜,會分別經(jīng)歷這3種重排序,如下所示:

上圖中的1屬于編繹器重排序押蚤,2和3屬于處理器重排序蔑歌。

3.1 內(nèi)存屏障

Memory Barrier,又稱內(nèi)存柵欄揽碘,是一個CPU指令次屠,基本上它是一條這樣的指令:
1、保證特定操作的執(zhí)行順序雳刺。
2劫灶、影響某些數(shù)據(jù)(或則是某條指令的執(zhí)行結(jié)果)的內(nèi)存可見性。

為了保證內(nèi)存可見性掖桦,Java編繹器在生成指令序列的適當(dāng)位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序本昏。JMM把內(nèi)存屏障分為4類,如下所示:

3.2 happens-before

從jdk5開始枪汪,java使用新的JSR-133內(nèi)存模型涌穆,基于happens-before的概念來闡述操作之間的內(nèi)存可見性。

在JMM中料饥,如果一個操作的執(zhí)行結(jié)果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關(guān)系朱监,這個的兩個操作既可以在同一個線程岸啡,也可以在不同的兩個線程中。

與程序員密切相關(guān)的happens-before規(guī)則如下:
1赫编、程序順序規(guī)則:一個線程中的每個操作巡蘸,happens-before于該線程中任意的后續(xù)操作奋隶。
2、監(jiān)視器鎖規(guī)則:對一個鎖的解鎖操作悦荒,happens-before于隨后對這個鎖的加鎖操作唯欣。
3、volatile域規(guī)則:對一個volatile域的寫操作搬味,happens-before于任意線程后續(xù)對這個volatile域的讀境氢。
4、傳遞性規(guī)則:如果 A happens-before B碰纬,且 B happens-before C萍聊,那么A happens-before C。

注意:兩個操作之間具有happens-before關(guān)系悦析,并不意味前一個操作必須要在后一個操作之前執(zhí)行寿桨!僅僅要求前一個操作的執(zhí)行結(jié)果,對于后一個操作是可見的强戴,且前一個操作按順序排在后一個操作之前亭螟。

3.3 數(shù)據(jù)依賴性

如果兩個操作訪問同一個變量,且這兩個操作中有一個為寫操作骑歹,此時這兩個操作之間就存在數(shù)據(jù)依賴性预烙。數(shù)據(jù)依賴性分為下列3種類型,如下所示:

對于上面的3種情況陵刹,只要重排序兩個操作的執(zhí)行順序默伍,程序的執(zhí)行結(jié)果就會被改變。編繹器和處理器在重排序時衰琐,會遵守數(shù)據(jù)依賴性也糊,而不會改變存在數(shù)據(jù)依賴關(guān)系的兩個操作的執(zhí)行順序。

3.4 as-if-serial

as-if-serial語義的意思是:不管怎么重排序(編繹器和處理器為了提高并行度)羡宙,(單線程)程序的執(zhí)行結(jié)果不能被改變狸剃。編繹器,runtime和處理器都必須遵守as-if-serial語義狗热。

3.5 示例

為了具體說明钞馁,請看下面計算圓面積的代碼示例:

double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r * r; //C

上面3個操作的數(shù)據(jù)依賴關(guān)系如下圖所示:

A和C之間存在數(shù)據(jù)依賴關(guān)系,同時B和C之間也存在數(shù)據(jù)依賴關(guān)系匿刮。因此在最終執(zhí)行的指令序列中僧凰,C不能被重排序到A和B的前面(C排到A和B的前面,程序的結(jié)果將會被改變)熟丸。但A和B之間沒有數(shù)據(jù)依賴關(guān)系训措,編譯器和處理器可以重排序A和B之間的執(zhí)行順序。下圖是該程序的兩種執(zhí)行順序:

3.6 重排序?qū)Χ嗑€程的影響

先看如下示例代碼:

class ReorderExample {
    int     a    = 0;
    boolean flag = false;

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

    public void reader() {
        if (flag) { //3
            int i = a * a; //4
            //s……
        }
    }
}

flag變量是個標(biāo)記,用來標(biāo)識變量a是否已被寫入绩鸣。這里假設(shè)有兩個線程A和B怀大,A首先執(zhí)行writer()方法,隨后B線程接著執(zhí)行reader()方法呀闻。線程B在執(zhí)行操作4時化借,能否看到線程A在操作1對共享變量a的寫入呢?
答案是:不一定能看到捡多。
由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系蓖康,編譯器和處理器可以對這兩個操作重排序;同樣局服,操作3和操作4沒有數(shù)據(jù)依賴關(guān)系钓瞭,編譯器和處理器也可以對這兩個操作重排序。讓我們先來看看淫奔,當(dāng)操作1和操作2重排序時山涡,可能會產(chǎn)生什么效果?請看下面的程序執(zhí)行時序圖唆迁,如下圖所示:

上圖中操作1和操作2做了重排序鸭丛。程序執(zhí)行時,線程A首先寫標(biāo)記變量flag唐责,隨后線B讀這個變量鳞溉。由于條件判斷為真,線程B將讀取變量a鼠哥。此時熟菲,變量a還沒有被線程A寫入,在里多線程程序的語義被重排序破壞了朴恳!

下面再讓我們看看抄罕,當(dāng)操作3和操作4重排序時會產(chǎn)生什么效果(借助這個重排序,可便說明控制依賴性)于颖。下面是操作3和操作4重排序后呆贿,程序執(zhí)行的時序圖,如下圖所示:

在程序中森渐,操作3和操作4存在控制依賴關(guān)系做入。當(dāng)代碼中存在控制依賴性時,會影響指令序列執(zhí)行的并行度同衣。為此竟块,編譯器和處理器會采用猜測Speculation)執(zhí)行來克服控制相關(guān)性對并行度的影響。以處理器的猜測執(zhí)行為例耐齐,執(zhí)行線程B的處理器可以提前讀取并計算a*a浪秘,然后把計算結(jié)果臨時保存到一個名為重排序緩沖(Reorder Buffer前弯,ROB)的硬件緩存中。當(dāng)操作3的條件判斷為真時秫逝,就把該計算結(jié)果寫入變量i中。

從上圖中我們可以看出询枚,猜測執(zhí)行實質(zhì)上對操作3和4做了重排序违帆。重排序在這里破壞了多線程程序的語義!

通過上例可以得出結(jié)論:在單線程程序中金蜀,對存在控制依賴的操作重排序刷后,不會改變執(zhí)行結(jié)果(這也是as-if-serial語義允許對存在控制依賴的操作做重排序的原因);但在多線程程序中渊抄,對存在控制依賴的操作重排序尝胆,可能會改變程序的執(zhí)行結(jié)果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末护桦,一起剝皮案震驚了整個濱河市含衔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌二庵,老刑警劉巖贪染,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚乡,死亡現(xiàn)場離奇詭異械筛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铜跑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進(jìn)店門因妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痰憎,“玉大人,你說我怎么就攤上這事攀涵∠吃牛” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵汁果,是天一觀的道長涡拘。 經(jīng)常有香客問我,道長据德,這世上最難降的妖魔是什么鳄乏? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮棘利,結(jié)果婚禮上橱野,老公的妹妹穿的比我還像新娘。我一直安慰自己善玫,他們只是感情好水援,可當(dāng)我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布密强。 她就那樣靜靜地躺著,像睡著了一般蜗元。 火紅的嫁衣襯著肌膚如雪或渤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天奕扣,我揣著相機(jī)與錄音薪鹦,去河邊找鬼。 笑死惯豆,一個胖子當(dāng)著我的面吹牛池磁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播楷兽,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼地熄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芯杀?” 一聲冷哼從身側(cè)響起端考,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎揭厚,沒想到半個月后跛梗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棋弥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年核偿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顽染。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡漾岳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粉寞,到底是詐尸還是另有隱情尼荆,我是刑警寧澤,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布唧垦,位于F島的核電站捅儒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏振亮。R本人自食惡果不足惜巧还,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坊秸。 院中可真熱鬧麸祷,春花似錦、人聲如沸褒搔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至走孽,卻和暖如春惧辈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磕瓷。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工咬像, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人生宛。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像肮柜,于是被迫代替她去往敵國和親陷舅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容