溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

從5個方面讓你真正了解Java內(nèi)存模型

發(fā)布時間:2020-06-21 12:26:48 來源:網(wǎng)絡(luò) 閱讀:331 作者:架構(gòu)師追風(fēng) 欄目:編程語言

前言

首先我們在了解java內(nèi)存模型之前先看一下計算機(jī)內(nèi)存模型,理解了計算機(jī)內(nèi)存模型的話后面在看JMM就會簡單的多。

從5個方面讓你真正了解Java內(nèi)存模型

計算機(jī)內(nèi)存

計算機(jī)是由CPU、主存、磁盤等組成的(簡單引出問題熬)我們都知道計算機(jī)執(zhí)行程序的指令都是由CPU來執(zhí)行的,執(zhí)行的時候是要處理數(shù)據(jù)的,這些數(shù)據(jù)通常存儲在主存中。

從5個方面讓你真正了解Java內(nèi)存模型

如圖所示,這時候問題來了,CPU的執(zhí)行速度越來越快,然后內(nèi)存倒是沒什么進(jìn)展,這樣的話CPU的讀寫操作就會非常耗時,效率不就很低了?

所以這個時候就出現(xiàn)了高速緩存(Cache)來解決這個問題,那么緩存是什么呢?緩存其實就是保存的數(shù)據(jù)備存,特點是快。所以這個時候程序的執(zhí)行過程就變成了這個樣子:首先在運行的時候會把數(shù)據(jù)從主存中賦值一份放在緩存中,然后CPU在運算的時候就直接去緩存中讀寫數(shù)據(jù),等執(zhí)行結(jié)束后在把數(shù)據(jù)刷新到主存中。這樣一來就大大的提高了執(zhí)行的速度。我們來看一下流程圖:

從5個方面讓你真正了解Java內(nèi)存模型

可以看出,運行的時候L1緩存先把數(shù)據(jù)從主存中讀取出來,然后CPU操作的數(shù)據(jù)是從緩存中讀取,當(dāng)數(shù)據(jù)執(zhí)行完畢,在從緩存中刷新到主存中。隨著CPU的執(zhí)行能力越來越強(qiáng),一層緩存已經(jīng)滿足不了需求了,這時候就出現(xiàn)了2級緩存(L2Cache)3級緩存(L3Cache),每級緩存都存儲的是下一級緩存的一部分?jǐn)?shù)據(jù)。

那么當(dāng)CPU需要數(shù)據(jù)的時候就會這樣執(zhí)行:首先去一級緩存(L1Cache)查找,如果一級緩存沒有就去二級緩存(L2Cache)查找,二級緩存沒有就去三級緩存(L3Cache)查找,如果緩存中沒有,就去主存中查找。 那么問題來了。

緩存一致性

現(xiàn)代計算機(jī)已經(jīng)不是單個CPU,有多個CPU每個CPU還可能會有多核,單核CPU只有一套緩存分別就是上面所說的L1、L2、L3如圖所示:

從5個方面讓你真正了解Java內(nèi)存模型

如果CPU有多個核心的話,就是每個核心都有L1緩存或者有L2緩存,而共享L3緩存或者L2緩存。

我們來看一下結(jié)構(gòu)圖:

從5個方面讓你真正了解Java內(nèi)存模型

這個時候每個核心都有自己的高速緩存,它們又共享同一主存,就會造成緩存一致性的問題,在多線程同時訪問同一共享數(shù)據(jù)的情況下,每個線程都是操作自己緩存的數(shù)據(jù)副本,這個時候就會出現(xiàn)每個緩存中的共享數(shù)據(jù)存在不一致的情況。多個處理器運算任務(wù)都涉及同一塊主存,需要一種協(xié)議可以保障數(shù)據(jù)的一致性,這類協(xié)議有MSI、MESI、MOSI及Dragon Protocol等。

處理器優(yōu)化

上面了解到提高CPU的效率就是在CPU和主存直接增加高速緩存,增加高速緩存會造成緩存不一致的問題,除了緩存不一致的問題,還有一種問題就是為了能讓處理器內(nèi)部的運算單元能夠盡量的被充分利用處理器可能會對輸入代碼進(jìn)行亂序執(zhí)行,并且處理器會在計算之后將亂序的代碼進(jìn)行結(jié)果重組來保證結(jié)果的一致性。在Java虛擬機(jī)中也有類似的指令重排序。

思考

這篇文章其實是講述java內(nèi)存模型的,為什么會和計算機(jī)硬件扯上關(guān)系呢?注意到上面有說到多線程的情況下會造成緩存不一致的問題,提到多線程就離不開并發(fā),想到并發(fā)的話就離不開三大問題,可見性,原子性,有序性的問題。那這三種特性不就是上面所說到的緩存不一致,處理器優(yōu)化和指令重排序問題嗎。這這樣看來緩存不一致不就是可見性的問題,而原子性不就是處理器優(yōu)化所導(dǎo)致的原子性問題,指令重排序就是導(dǎo)致有序性的問題。那么Java內(nèi)存模型又是什么呢?

Java內(nèi)存模型

Java內(nèi)存模型的作用就是用來屏蔽掉不同操作系統(tǒng)中的內(nèi)存差異性來保持并發(fā)的一致性。同時JMM也規(guī)范了JVM如何與計算機(jī)內(nèi)存進(jìn)行交互。簡單的來說java內(nèi)存模型就是Java自己的一套協(xié)議來屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,實現(xiàn)平臺一致性達(dá)到最終的"一次編寫,到處運行"??吹竭@里就知道了Jmm是用來做什么的。同時Java內(nèi)存模型可以理解為java并發(fā)內(nèi)存模型。然后JMM

通信

Java內(nèi)存模型(以下簡稱JMM)規(guī)定了,所有變量都存儲在主內(nèi)存中,每個線程都有自己的本地緩存,所以線程中對變量的操作都必須在本地緩存中進(jìn)行并不是直接操作主內(nèi)存,線程之間的無法訪問對方線程的變量,想要通信的話就只能通過主內(nèi)存進(jìn)行通信。

JMM抽象示意圖:

從5個方面讓你真正了解Java內(nèi)存模型

從上圖可以看出每個線程都有一個本地內(nèi)存,如果線程想要通信的話要執(zhí)行一下步驟:

  • A線程先把本地內(nèi)存的值寫入主內(nèi)存

  • B線程從主內(nèi)存中去讀取出A線程寫的值

到這里就對JMM有個清晰的理解了。JMM其實是一種規(guī)范,其主要目的就是為了解決多線程通過共享內(nèi)存進(jìn)行通信時所產(chǎn)生的本地內(nèi)存數(shù)據(jù)不一致,編譯器會對代碼指令重排序、處理器會對代碼亂序執(zhí)行等帶來的問題。歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會在里面更新,整理的資料也會放在里面。

解決的問題

JMM所解決的問題離不開我們上面所說的三大特性:可見性、原子性、有序性.

原子性:在java中使用synchronized關(guān)鍵字保證代碼的原子性。

可見性:volatile關(guān)鍵字保證了多線程操控變量的可見性,同時synchronized和final也可以保證變量的可見性,注意:volatile并不保證原子性,所以什么時候用volatile一定要注意。

有序性:volatile可以禁用指令重排,synchronized關(guān)鍵字保證同一時刻只允許一條線程操作所以我們可以發(fā)現(xiàn)synchronized可以解決三種問題,所以使用synchronized關(guān)鍵字比較多,但是synchronized只允許一個線程進(jìn)行操作,會造成上下文切換的效率問題。

最后

歡迎大家一起交流,喜歡文章記得點個贊喲,感謝支持!


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI