您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么理解Java內(nèi)存模型”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么理解Java內(nèi)存模型”吧!
內(nèi)存模型的由來
1. 計(jì)算機(jī)在執(zhí)行程序的時候每條指令都是由CPU來執(zhí)行的。而CPU在執(zhí)行的時候?yàn)榱双@取數(shù)據(jù),所以難免與主存打交道。
2. 隨著CPU技術(shù)的發(fā)展其執(zhí)行越來越高速度,越來也快,同時因內(nèi)存技術(shù)發(fā)展比較緩慢,性能沒有太大的變化,所以導(dǎo)致出現(xiàn)CPU每次操作內(nèi)存都需要耗費(fèi)一定的等待時間。
3. 為在保證CPU技術(shù)發(fā)展同時優(yōu)化解決這一問題,人們后來想出來了一個方案,就是在CPU和內(nèi)存之間增加高速緩沖存儲器(Cache)。
高速緩沖存儲器是存在于主存與CPU之間的一級存儲器, 由靜態(tài)存儲芯片(SRAM)組成,容量比較小但速度比主存高得多, 接近于CPU的速度。在計(jì)算機(jī)存儲系統(tǒng)的層次結(jié)構(gòu)中,是介于中央處理器和主存儲器之間的高速小容量存儲器。它和主存儲器一起構(gòu)成一級的存儲器。高速緩沖存儲器和主存儲器之間信息的調(diào)度和傳送是由硬件自動進(jìn)行的。
因?yàn)镃ache速度接近于CPU的速度且CPU每次操作主存前都會先訪問Cache,所以通過增加Cache后當(dāng)便達(dá)到了優(yōu)化的效果。
4. 隨著CPU的升級,一層緩存慢慢地?zé)o法滿足要求,因此逐漸地衍生出多級緩存。每一級緩存中所儲存的全部數(shù)據(jù)都是下一級緩存的一部分。而CPU讀取數(shù)據(jù)也演變?yōu)椋寒?dāng)CPU要讀取一個數(shù)據(jù)時,首先從一級緩存中查找,如果沒有找到再從二級緩存中查找,如果還是沒有就從下一級緩存查直到訪問內(nèi)存。如下圖所示
5. 單核CPU只含有一套L1,L2,L3緩存;如果CPU含有多個核心,即多核CPU,則每個核心都含有一套L1(甚至和L2)緩存,而共享L3(或者和L2)緩存,下圖是一個單CPU雙核得緩存結(jié)構(gòu)圖:
隨著計(jì)算機(jī)能力不斷提升,開始支持多線程,那么就可能會問題了。我們分別來分析下單線程、多線程在單核CPU、多核CPU中的影響。
單核cpu與單線程:核心的緩存只被一個線程訪問,緩存獨(dú)占,不會出現(xiàn)訪問沖突等問題。
單核CPU與多線程:進(jìn)程中的多個線程會同時訪問進(jìn)程中的共享數(shù)據(jù),CPU將某塊內(nèi)存加載到緩存后,不同線程在訪問相同的物理地址的時候,都會映射到相同的緩存位置,這樣即使發(fā)生線程的切換,緩存仍然不會失效。但由于任何時刻只能有一個線程在執(zhí)行,因此不會出現(xiàn)緩存訪問沖突。
多核CPU與多線程:每個核都至少有一個L1 緩存用于提升效率。當(dāng)多個線程分別在不同的核心上執(zhí)行且訪問進(jìn)程中的同個共享內(nèi)存,由于多核是可以并行的,則可能會出現(xiàn)類似多線程編程中出現(xiàn)的并發(fā)問題,如對于同一塊內(nèi)存中的變量,多個核心同時讀寫修改數(shù)據(jù)的話,就會出現(xiàn)不可預(yù)期的錯誤,而其解決思路則是通過鎖機(jī)制。
所以在CPU和主存之間增加緩存,在多核CPU多線程場景下發(fā)生并發(fā)內(nèi)存訪問操作時可能會出現(xiàn)歧義。
處理器優(yōu)化--“指令重排”
除了上面的問題之外,還有另一個硬件問題也比較重要:處理器為了使其內(nèi)部的運(yùn)算單元能夠被充分利用會進(jìn)行優(yōu)化,可能會亂序執(zhí)行處理輸入代碼,此處暫理解為“指令重排”。除了一些處理器會對代碼進(jìn)行優(yōu)化亂序處理外,很多編程語言的編譯器也會有類似的優(yōu)化,比如Java虛擬機(jī)的JIT即時編譯器也會做指令重排。
如Java單例設(shè)計(jì)模式Double-Check例子中的voliate關(guān)鍵字應(yīng)用就是為了防止因指令重排導(dǎo)致在多線程并發(fā)場景下出現(xiàn)異常。感興趣的朋友可以參考我的另一篇文章深入解析單例模式--懶漢模式,這里就不再進(jìn)行過多的探討。
什么是內(nèi)存模型
上面分析了那么多其實(shí)目的是為了引出兩個重要問題:
多核CPU多線程場景下發(fā)生并發(fā)內(nèi)存訪問操作時可能會出現(xiàn)歧義
處理器為了使其內(nèi)部的運(yùn)算單元能夠被充分利用會自行進(jìn)行優(yōu)化--“指令重排”
那么對于以上問題該如何解決?這時候便引出了重要概念--內(nèi)存模型,定義如下:
內(nèi)存模型是對內(nèi)存進(jìn)行讀寫訪問過程的抽象,可以理解為內(nèi)存模型定義了共享內(nèi)存系統(tǒng)中讀寫操作行為的規(guī)范,通過這些規(guī)則來規(guī)范對內(nèi)存的讀寫操作,從而保證指令執(zhí)行的正確性。它與處理器有關(guān)、與緩存有關(guān)、與并發(fā)有關(guān)、與編譯器也有關(guān)。目的是為解決CPU多級緩存、處理器優(yōu)化、指令重排等導(dǎo)致的問題與歧義。 |
可以簡單理解為內(nèi)存模型其實(shí)就是解決多線程場景下因并發(fā)所導(dǎo)致的問題的一個重要規(guī)范。
Java內(nèi)存模型
1. 定義
Java內(nèi)存模型(Java Memory Model ,JMM)就是一種符合內(nèi)存模型規(guī)范的,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的,以實(shí)現(xiàn)讓Java程序在各種平臺下都能達(dá)到一致的內(nèi)存訪問效果的規(guī)范。
2. 理解
Java內(nèi)存模型(簡稱“JMM”)是一個規(guī)范,其主要目的是定義程序中各種變量的訪問規(guī)則,是圍繞著在并發(fā)過程中如何處理原子性、可見性和有序性三個特征來建立的。
關(guān)于Java內(nèi)存模型的實(shí)現(xiàn),相信熟悉Java并發(fā)編程的朋友一定會熟悉,Java提供了一系列和并發(fā)處理相關(guān)的關(guān)鍵字,其實(shí)這些就是Java內(nèi)存模型封裝了底層的實(shí)現(xiàn)后提供給程序員使用的一些關(guān)鍵字,本文在這里就不對這些關(guān)鍵字一一展開討論了,感興趣的朋友可以看看《Java多線程編程核心技術(shù)》進(jìn)行了解。其中如:
通過使用volatile關(guān)鍵字解決因指令重排導(dǎo)致的問題
通過synchronized關(guān)鍵字來保證線程安全等
等方式其實(shí)就與硬件通過計(jì)算機(jī)內(nèi)存模型中限制處理器優(yōu)化和使用內(nèi)存屏障等解決問題的思路一致。
到此,相信大家對“怎么理解Java內(nèi)存模型”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。