更新時間:2020-08-10 15:23:28 來源:動力節(jié)點 瀏覽2237次
Java內(nèi)存模型JMM基礎(chǔ)知識及原理,學(xué)習(xí)過程中需要了解內(nèi)存模型抽象結(jié)構(gòu)、共享變量、JMM抽象結(jié)構(gòu)模型、主內(nèi)存與工作內(nèi)存的相關(guān)知識,Java內(nèi)存模型具有原子性、可見性、有序性三大特征。
一、內(nèi)存模型抽象結(jié)構(gòu)
線程間協(xié)作通信可以類比人與人之間的協(xié)作的方式,在現(xiàn)實生活中,之前網(wǎng)上有個流行語“你媽喊你回家吃飯了”,就以這個生活場景為例,小明在外面玩耍,小明媽媽在家里做飯,做完飯后準備叫小明回家吃飯,那么就存在兩種方式:
小明媽媽要去上班了十分緊急這個時候手機又沒有電了,于是就在桌子上貼了一張紙條“飯做好了,放在…”小明回家后看到紙條如愿吃到媽媽做的飯菜,那么,如果將小明媽媽和小明作為兩個線程,那么這張紙條就是這兩個線程間通信的共享變量,通過讀寫共享變量實現(xiàn)兩個線程間協(xié)作;
還有一種方式就是,媽媽的手機還有電,媽媽在趕去坐公交的路上給小明打了個電話,這種方式就是通知機制來完成協(xié)作。同樣,可以引申到線程間通信機制。
通過上面這個例子,應(yīng)該有些認識。在并發(fā)編程中主要需要解決兩個問題:1.線程之間如何通信;2.線程之間如何完成同步。通信是指線程之間以何種機制來交換信息,主要有兩種:共享內(nèi)存和消息傳遞。可以分別類比上面的兩個舉例。Java內(nèi)存模型是共享內(nèi)存的并發(fā)模型,線程之間主要通過讀-寫共享變量來完成隱式通信。如果程序員不能理解Java的共享內(nèi)存模型在編寫并發(fā)程序時一定會遇到各種各樣關(guān)于內(nèi)存可見性的問題。
二、共享變量
在Java程序中所有實例域,靜態(tài)域和數(shù)組元素都是放在堆內(nèi)存中(所有線程均可訪問到,是可以共享的),而局部變量,方法定義參數(shù)和異常處理器參數(shù)不會在線程間共享。共享數(shù)據(jù)會出現(xiàn)線程安全的問題,而非共享數(shù)據(jù)不會出現(xiàn)線程安全的問題。
三、JMM抽象結(jié)構(gòu)模型
CPU的處理速度和主存的讀寫速度不是一個量級的(CPU的處理速度快很多),為了平衡這種巨大的差距,每個CPU都會有緩存。因此,共享變量會先放在主存中,每個線程都有屬于自己的工作內(nèi)存,并且會把位于主存中的共享變量拷貝到自己的工作內(nèi)存,之后的讀寫操作均使用位于工作內(nèi)存的變量副本,并在某個時刻將工作內(nèi)存的變量副本寫回到主存中去。JMM就從抽象層次定義了這種方式,并且JMM決定了一個線程對共享變量的寫入何時對其他線程是可見的。
如圖為JMM抽象示意圖,線程A和線程B之間要完成通信的話,要經(jīng)歷如下兩步:
線程A從主內(nèi)存中將共享變量讀入線程A的工作內(nèi)存后并進行操作,之后將數(shù)據(jù)重新寫回到主內(nèi)存中;線程B從主存中讀取最新的共享變量。
從橫向去看看,線程A和線程B就好像通過共享變量在進行隱式通信。這其中有個意思的問題,如果線程A更新后數(shù)據(jù)并沒有及時寫回到主存,而此時線程B讀到的是過期的數(shù)據(jù),這就出現(xiàn)了“臟讀”現(xiàn)象。可以通過同步機制來解決或者通過volatile關(guān)鍵字使得每次volatile變量都能夠強制刷新到主存,從而對每個線程都是可見的。
四、主內(nèi)存與工作內(nèi)存
處理器上的寄存器的讀寫的速度比內(nèi)存快幾個數(shù)量級,為了解決這種速度矛盾,在它們之間加入了高速緩存。加入高速緩存帶來了一個新的問題:緩存一致性。如果多個緩存共享同一塊主內(nèi)存區(qū)域,那么多個緩存的數(shù)據(jù)可能會不一致,需要一些協(xié)議來解決這個問題。
所有的變量都存儲在主內(nèi)存中,每個線程還有自己的工作內(nèi)存,工作內(nèi)存存儲在高速緩存或者寄存器中,保存了該線程使用的變量的主內(nèi)存副本拷貝。
線程只能直接操作工作內(nèi)存中的變量,不同線程之間的變量值傳遞需要通過主內(nèi)存來完成。
五、內(nèi)存間交互操作
Java內(nèi)存模型定義了8個操作來完成主內(nèi)存和工作內(nèi)存的交互操作。
lock(鎖定):作用于主內(nèi)存中的變量,它把一個變量標識為一個線程獨占的狀態(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)虛擬機遇到一個需要使用到變量的值的字節(jié)碼指令時將會執(zhí)行這個操作;
assign(賦值):作用于工作內(nèi)存中的變量,它把一個從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機遇到一個給變量賦值的字節(jié)碼指令時執(zhí)行這個操作;
store(存儲):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個變量的值傳送給主內(nèi)存中以便隨后的write操作使用;
write(操作):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。
以上就是動力節(jié)點java培訓(xùn)機構(gòu)的小編針對“Java從入門到項目實踐:Java內(nèi)存模型JMM”的內(nèi)容進行的回答,希望對大家有所幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。
相關(guān)閱讀