Java并發(fā)之一:Java內(nèi)存模型(Memory Model)

Java內(nèi)存模型,Java Memory Model,我個(gè)人更喜歡“Java存儲(chǔ)模型”的譯法。

介紹

如前所述,JVM被設(shè)計(jì)成一臺(tái)抽象的虛擬計(jì)算機(jī),JVM的并發(fā)問(wèn)題及解決方案與物理計(jì)算機(jī)中的并發(fā)問(wèn)題有很多相似之處。

由于現(xiàn)代計(jì)算機(jī)的內(nèi)存與CPU在運(yùn)算速度上的巨大差別,通常會(huì)加入一層更接近CPU讀寫(xiě)速度的高速緩存(Cache),將運(yùn)算使用到的數(shù)據(jù)復(fù)制到Cache中,讓運(yùn)算能加速進(jìn)行,運(yùn)算結(jié)束后再?gòu)腃ache同步回內(nèi)存之中。這個(gè)設(shè)計(jì)解決了速度的矛盾,也帶來(lái)了一個(gè)新的問(wèn)題:緩存一致性。在多CPU系統(tǒng)中,每個(gè)CPU都有自己的Cache,它們共享同一主內(nèi)存,當(dāng)多個(gè)CPU的運(yùn)算任務(wù)涉及同一塊內(nèi)存區(qū)域時(shí),就可能導(dǎo)致各自的Cache數(shù)據(jù)不一致。對(duì)此又設(shè)計(jì)了“緩存一致性協(xié)議”,如MESI,要求各個(gè)CPU在讀寫(xiě)Cache時(shí),都遵循這些協(xié)議進(jìn)行操作,以解決緩存數(shù)據(jù)不一致的問(wèn)題?!皟?nèi)存模型”(Memory Model),就是在特定的協(xié)議下,對(duì)特定的高速緩存讀寫(xiě)訪問(wèn)過(guò)程進(jìn)行抽象。不同架構(gòu)的物理機(jī)器擁有不一樣的內(nèi)存模式,而Java虛擬機(jī)也有自己的內(nèi)存模型,其基礎(chǔ)原理是共通的。

此外,為了充分利用CPU的運(yùn)算單元,可能會(huì)對(duì)輸入代碼進(jìn)行亂序執(zhí)行優(yōu)化,只保證執(zhí)行結(jié)果的一致性,但不保證各語(yǔ)句的計(jì)算順序與輸入代碼一致。Java虛擬機(jī)的即時(shí)編譯器也有類(lèi)似的指令重排序優(yōu)化。

Java語(yǔ)言規(guī)范中試圖定義一種Java內(nèi)存模型,以實(shí)現(xiàn)Java程序在各種平臺(tái)上能達(dá)到一致的內(nèi)存訪問(wèn)效果,為多線程的同步和避免數(shù)據(jù)爭(zhēng)用提供一套有效平衡高吞吐與一致性的保障機(jī)制。

什么是Java內(nèi)存模型

一個(gè)內(nèi)存模型描述的是,給定程序的某個(gè)特定的執(zhí)行軌跡(trace)是否是該程序的一個(gè)合法執(zhí)行。Java內(nèi)存模型檢查執(zhí)行軌跡中的每次讀操作,以及根據(jù)特定規(guī)則,校驗(yàn)該讀操作觀察到的寫(xiě)是否合法。內(nèi)存模型描述了一個(gè)程序的可預(yù)期行為,具體實(shí)現(xiàn)時(shí)擁有充分的自由度去生成需要的代碼,只要其最終執(zhí)行結(jié)果可經(jīng)由內(nèi)存模型進(jìn)行推測(cè)。

通俗地講,Java內(nèi)存模型(JMM)定義了一系列規(guī)則,以確保某一線程的寫(xiě)操作能正確呈現(xiàn)給其他線程。JMM并沒(méi)有描述多線程該如何執(zhí)行,而是描述多線程允許的行為。

JMM規(guī)定了所有的共享變量都存儲(chǔ)在JVM的主內(nèi)存中。每條線程有自己的工作內(nèi)存,用于保存該線程用到的變量的主內(nèi)存副本拷貝(具體實(shí)現(xiàn)通常不會(huì)拷貝整個(gè)對(duì)象),線程對(duì)變量的操作全部在工作內(nèi)存中進(jìn)行,不能直接讀寫(xiě)主內(nèi)存中的變量。不同的線程間也無(wú)法直接訪問(wèn)對(duì)方的工作內(nèi)存,線程間變量值的傳遞需要通過(guò)主內(nèi)存來(lái)完成。

關(guān)于主內(nèi)存和工作內(nèi)存之間的具體交互協(xié)議,即一個(gè)變量如何從主內(nèi)存拷貝到工作內(nèi)存、如何從工作內(nèi)存同步回主內(nèi)存之類(lèi)的實(shí)現(xiàn)細(xì)節(jié),JMM定義了8種操作(lock,unlock,read,load,use,assign,store,write),虛擬機(jī)實(shí)現(xiàn)時(shí)必須保證以上每一種操作都是原子的、不可再分的。

共享變量/堆內(nèi)存:能夠在線程間共享的內(nèi)存稱(chēng)作“共享內(nèi)存或堆內(nèi)存”。所有的實(shí)例域、靜態(tài)域以及數(shù)組元素都存儲(chǔ)在堆內(nèi)存中。方法中的局部變量永遠(yuǎn)不會(huì)被線程間共享,也不會(huì)受內(nèi)存模型影響。我們也無(wú)需關(guān)心線程內(nèi)動(dòng)作,每個(gè)單線程都應(yīng)遵守正確的線程內(nèi)語(yǔ)義。

線程間的動(dòng)作:線程間的動(dòng)作是由某一線程執(zhí)行,能被另一線程探測(cè)或直接影響的動(dòng)作。包括:

共享變量的讀/寫(xiě)
同步動(dòng)作 (synchronization action)
? ? ? ?lock/unlock某個(gè)monitor;
? ? ? ?讀寫(xiě)某個(gè)volatile變量
啟動(dòng)一個(gè)線程
與外部交互的動(dòng)作
導(dǎo)致某個(gè)線程進(jìn)入無(wú)限循環(huán)的動(dòng)作(thread devergence action)


延伸閱讀

本文參考了《深入理解Java虛擬機(jī)》、《JSR-133-Memory?Model》,有充裕時(shí)間的同學(xué)可以去完整過(guò)一遍,但原文有較多艱澀難懂之處。這里做了較大的裁剪,隱藏更深一層的信息,以求對(duì)Java內(nèi)存模型能有一個(gè)快速又不過(guò)于淺顯的了解。更多的概念和規(guī)則:

緩存一致性,Cache Coherence,MESI協(xié)議
Instruction Reorder,指令重排序
Happens-Before?關(guān)系規(guī)則
As-If-Serial ,線程內(nèi)串行的語(yǔ)義
Memory Barrier,內(nèi)存屏障


參考資料:

? ? ?JSR-133 Java Memory Model
? ? 《Java語(yǔ)言規(guī)范SE8》第17章 線程與鎖 17.4 內(nèi)存模型
? ? 《深入理解Java虛擬機(jī)》第12章 Java內(nèi)存模型與線程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容