更新時(shí)間:2020-08-10 15:23:28 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽2567次
Java內(nèi)存模型JMM基礎(chǔ)知識(shí)及原理,學(xué)習(xí)過(guò)程中需要了解內(nèi)存模型抽象結(jié)構(gòu)、共享變量、JMM抽象結(jié)構(gòu)模型、主內(nèi)存與工作內(nèi)存的相關(guān)知識(shí),Java內(nèi)存模型具有原子性、可見(jiàn)性、有序性三大特征。

一、內(nèi)存模型抽象結(jié)構(gòu)
線程間協(xié)作通信可以類比人與人之間的協(xié)作的方式,在現(xiàn)實(shí)生活中,之前網(wǎng)上有個(gè)流行語(yǔ)“你媽喊你回家吃飯了”,就以這個(gè)生活場(chǎng)景為例,小明在外面玩耍,小明媽媽在家里做飯,做完飯后準(zhǔn)備叫小明回家吃飯,那么就存在兩種方式:
小明媽媽要去上班了十分緊急這個(gè)時(shí)候手機(jī)又沒(méi)有電了,于是就在桌子上貼了一張紙條“飯做好了,放在…”小明回家后看到紙條如愿吃到媽媽做的飯菜,那么,如果將小明媽媽和小明作為兩個(gè)線程,那么這張紙條就是這兩個(gè)線程間通信的共享變量,通過(guò)讀寫共享變量實(shí)現(xiàn)兩個(gè)線程間協(xié)作;
還有一種方式就是,媽媽的手機(jī)還有電,媽媽在趕去坐公交的路上給小明打了個(gè)電話,這種方式就是通知機(jī)制來(lái)完成協(xié)作。同樣,可以引申到線程間通信機(jī)制。
通過(guò)上面這個(gè)例子,應(yīng)該有些認(rèn)識(shí)。在并發(fā)編程中主要需要解決兩個(gè)問(wèn)題:1.線程之間如何通信;2.線程之間如何完成同步。通信是指線程之間以何種機(jī)制來(lái)交換信息,主要有兩種:共享內(nèi)存和消息傳遞。可以分別類比上面的兩個(gè)舉例。Java內(nèi)存模型是共享內(nèi)存的并發(fā)模型,線程之間主要通過(guò)讀-寫共享變量來(lái)完成隱式通信。如果程序員不能理解Java的共享內(nèi)存模型在編寫并發(fā)程序時(shí)一定會(huì)遇到各種各樣關(guān)于內(nèi)存可見(jiàn)性的問(wèn)題。
二、共享變量
在Java程序中所有實(shí)例域,靜態(tài)域和數(shù)組元素都是放在堆內(nèi)存中(所有線程均可訪問(wèn)到,是可以共享的),而局部變量,方法定義參數(shù)和異常處理器參數(shù)不會(huì)在線程間共享。共享數(shù)據(jù)會(huì)出現(xiàn)線程安全的問(wèn)題,而非共享數(shù)據(jù)不會(huì)出現(xiàn)線程安全的問(wèn)題。
三、JMM抽象結(jié)構(gòu)模型
CPU的處理速度和主存的讀寫速度不是一個(gè)量級(jí)的(CPU的處理速度快很多),為了平衡這種巨大的差距,每個(gè)CPU都會(huì)有緩存。因此,共享變量會(huì)先放在主存中,每個(gè)線程都有屬于自己的工作內(nèi)存,并且會(huì)把位于主存中的共享變量拷貝到自己的工作內(nèi)存,之后的讀寫操作均使用位于工作內(nèi)存的變量副本,并在某個(gè)時(shí)刻將工作內(nèi)存的變量副本寫回到主存中去。JMM就從抽象層次定義了這種方式,并且JMM決定了一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)其他線程是可見(jiàn)的。

如圖為JMM抽象示意圖,線程A和線程B之間要完成通信的話,要經(jīng)歷如下兩步:
線程A從主內(nèi)存中將共享變量讀入線程A的工作內(nèi)存后并進(jìn)行操作,之后將數(shù)據(jù)重新寫回到主內(nèi)存中;線程B從主存中讀取最新的共享變量。
從橫向去看看,線程A和線程B就好像通過(guò)共享變量在進(jìn)行隱式通信。這其中有個(gè)意思的問(wèn)題,如果線程A更新后數(shù)據(jù)并沒(méi)有及時(shí)寫回到主存,而此時(shí)線程B讀到的是過(guò)期的數(shù)據(jù),這就出現(xiàn)了“臟讀”現(xiàn)象??梢酝ㄟ^(guò)同步機(jī)制來(lái)解決或者通過(guò)volatile關(guān)鍵字使得每次volatile變量都能夠強(qiáng)制刷新到主存,從而對(duì)每個(gè)線程都是可見(jiàn)的。
四、主內(nèi)存與工作內(nèi)存
處理器上的寄存器的讀寫的速度比內(nèi)存快幾個(gè)數(shù)量級(jí),為了解決這種速度矛盾,在它們之間加入了高速緩存。加入高速緩存帶來(lái)了一個(gè)新的問(wèn)題:緩存一致性。如果多個(gè)緩存共享同一塊主內(nèi)存區(qū)域,那么多個(gè)緩存的數(shù)據(jù)可能會(huì)不一致,需要一些協(xié)議來(lái)解決這個(gè)問(wèn)題。

所有的變量都存儲(chǔ)在主內(nèi)存中,每個(gè)線程還有自己的工作內(nèi)存,工作內(nèi)存存儲(chǔ)在高速緩存或者寄存器中,保存了該線程使用的變量的主內(nèi)存副本拷貝。
線程只能直接操作工作內(nèi)存中的變量,不同線程之間的變量值傳遞需要通過(guò)主內(nèi)存來(lái)完成。
五、內(nèi)存間交互操作
Java內(nèi)存模型定義了8個(gè)操作來(lái)完成主內(nèi)存和工作內(nèi)存的交互操作。

lock(鎖定):作用于主內(nèi)存中的變量,它把一個(gè)變量標(biāo)識(shí)為一個(gè)線程獨(dú)占的狀態(tài);
unlock(解鎖):作用于主內(nèi)存中的變量,它把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后的變量才可以被其他線程鎖定
read(讀?。鹤饔糜谥鲀?nèi)存的變量,它把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便后面的load動(dòng)作使用;
load(載入):作用于工作內(nèi)存中的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存中的變量副本
use(使用):作用于工作內(nèi)存中的變量,它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作;
assign(賦值):作用于工作內(nèi)存中的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作;
store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值傳送給主內(nèi)存中以便隨后的write操作使用;
write(操作):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。
以上就是動(dòng)力節(jié)點(diǎn)java培訓(xùn)機(jī)構(gòu)的小編針對(duì)“Java從入門到項(xiàng)目實(shí)踐:Java內(nèi)存模型JMM”的內(nèi)容進(jìn)行的回答,希望對(duì)大家有所幫助,如有疑問(wèn),請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為你服務(wù)。
相關(guān)閱讀
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)