您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何理解Java內(nèi)存模型”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“如何理解Java內(nèi)存模型”吧!
很多人會把Java內(nèi)存區(qū)域(運行時數(shù)據(jù)區(qū))和Java內(nèi)存模型(JMM)搞混,這兩者是完全不一樣的東西。
Java內(nèi)存區(qū)域是指JVM運行時數(shù)據(jù)分區(qū)域存儲,而Java內(nèi)存模型是定義了線程和主內(nèi)存之間的抽象關(guān)系,了解Java內(nèi)存模型是學(xué)好Java并發(fā)編程的基礎(chǔ)。
Java內(nèi)存模型中規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程對變量的所有操作都必須在工作內(nèi)存中進行,而不能直接讀寫主內(nèi)存中的變量。我們來看一張圖:
每個線程擁有一個自己的私有工作內(nèi)存,需要變量時從主內(nèi)存中拷貝一份到工作內(nèi)存,如果更新過變量之后再將共享變量刷新到主內(nèi)存。
但是兩個線程之間,是沒有辦法讀取對方工作內(nèi)存中的變量值的。看一個例子:
public class Test { private static boolean flag=false; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { System.out.println("waiting"); while (!flag){} System.out.println("in"); } }).start(); Thread.sleep(2000); new Thread(new Runnable() { @Override public void run() { System.out.println("change flag"); flag=true; System.out.println("change success"); } }).start(); } }
首先定義了一個靜態(tài)變量flag為false,A線程等待flag等于true后輸出in,于是我們新開一個線程將flag修改為true。結(jié)果是A線程依舊無法輸出in。
原理看Java內(nèi)存模型的圖就理解了,不同的線程修改變量,對本地線程是不可見的。
通過上面這段代碼,我們已經(jīng)知道了Java內(nèi)存模型的結(jié)構(gòu),那么工作內(nèi)存和主內(nèi)存之間是如何讀取變量又是如何修改變量的呢?JMM提供了對變量的一系列原子操作。我們先不講理論,看個圖,這個圖描述了上面一段代碼的執(zhí)行過程:
整個過程一共十步,重復(fù)幾個步驟不講了,我把不重復(fù)的六個操作列一下:
read:從主內(nèi)存讀取數(shù)據(jù)
load:將主內(nèi)存讀取到的數(shù)據(jù)寫入工作內(nèi)存
use :從工作內(nèi)存中讀取數(shù)據(jù)來使用
assign:把計算好的值重新賦值到工作內(nèi)存中
store:將工作內(nèi)存數(shù)據(jù)寫入主內(nèi)存
write:將store過去的變量賦值給主內(nèi)存中的變量
通過上面的圖,對下面六個原子操作的理解應(yīng)該可以更加深刻了。JMM的原子操作一共有八個,下面列出剩下的兩個
lock:將主內(nèi)存變量加鎖,標(biāo)識為線程獨占狀態(tài)
unlock:將主內(nèi)存變量解鎖,解鎖后其他線程可以鎖定該變量
從前面的例子我們已經(jīng)看到了,一個線程修改完數(shù)據(jù),另外一個線程無法立即可見,這就是JMM緩存不一致的問題,有兩種解決辦法:
加鎖:
還記得我們沒有用到過的JMM原操作的最后兩個嗎,lock和unlock,使用這兩個操作就可以實現(xiàn)緩存一致性,一個線程想要獲取某個主內(nèi)存變量時,先使用lock將主內(nèi)存變量加鎖,只有他才能使用,等用完后再unlock,其他線程才能競爭。但是加鎖意味著性能低。
MESI緩存一致性協(xié)議:
這個協(xié)議涉及到cpu的總線嗅探機制,從上面的JMM執(zhí)行的流程圖中我們可以看到當(dāng)某個線程修改了共享變量后,他會回寫到主內(nèi)存,MESI緩存一致性協(xié)議就是通過cpu的總線嗅探機制,將其他也正在使用該變量的線程的數(shù)據(jù)失效掉,使得這些線程要重新讀取主內(nèi)存中的值,從而保證緩存最終一致性。
到此,相信大家對“如何理解Java內(nèi)存模型”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。