終于有人把Java內(nèi)存模型說清楚了_第1頁
終于有人把Java內(nèi)存模型說清楚了_第2頁
終于有人把Java內(nèi)存模型說清楚了_第3頁
終于有人把Java內(nèi)存模型說清楚了_第4頁
終于有人把Java內(nèi)存模型說清楚了_第5頁
已閱讀5頁,還剩4頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、【教程】終于有人把Java內(nèi)存模型說清楚本文就來整體的介紹一下Java內(nèi)存模型,讀完本文以后,你就知道到底Java內(nèi) 存模型是什么,為什么要有Java內(nèi)存模型,Java內(nèi)存模型解決了什么問題等。 本文中很多說法都是筆者自己理解后定義出來的。希望能夠讓讀者可以對Java 內(nèi)存模型有更加清晰的認(rèn)識。為什么要有內(nèi)存模型在介紹Java內(nèi)存模型之前,我們先來看一下到底什么是計算機(jī)內(nèi)存模型,然后 再來看Java內(nèi)存模型在計算機(jī)內(nèi)存模型的基礎(chǔ)上都做了哪些事情。要說計算機(jī)的內(nèi)存模型,就要說一段古老的歷史,看一下為什么要有內(nèi)存模型。 內(nèi)存模型:英文名Memory Model,它是一個老古董了。它是與計算機(jī)硬件有

2、關(guān) 的一個概念。那么,我先介紹下它和硬件到底有啥關(guān)系。CPU和緩存一致性我們應(yīng)該知道,計算機(jī)在執(zhí)行程序的時候,每條指令都是在CPU中執(zhí)行的,而 執(zhí)行的時候,又免不了和數(shù)據(jù)打交道。而計算機(jī)上面的數(shù)據(jù),是存放在主存當(dāng)中的,也就是計算機(jī)的物理內(nèi)存。剛開始,還相安無事,但是隨著CPU技術(shù)的發(fā)展,CPU的執(zhí)行速度越來越快。 而由于內(nèi)存的技術(shù)并沒有太大的變化,所以從內(nèi)存中讀取和寫入數(shù)據(jù)的過程和 CPU的執(zhí)行速度比起來差距就會越來越大,這就導(dǎo)致CPU每次操作內(nèi)存都要 耗費(fèi)很多等待時間。這就像一家創(chuàng)業(yè)公司,剛開始,創(chuàng)始人和員工之間工作關(guān)系其樂融融,但是隨著 創(chuàng)始人的能力和野心越來越大,逐漸和員工之間出現(xiàn)了差距

3、,普通員工越來越跟 不上CEO的腳步。老板的每一個命令,傳達(dá)到基層員工之后,由于基層員工的理解能力、執(zhí)行能力 的欠缺,就會耗費(fèi)很多時間。這也就無形中拖慢了整家公司的工作效率??墒牵荒芤?yàn)閮?nèi)存的讀寫速度慢,就不發(fā)展CPU技術(shù)了吧?總不能讓內(nèi)存成 為計算機(jī)處理的瓶頸吧?所以,人們想出來了一個好的辦法,就是在CPU和內(nèi)存之間增加高速緩存。緩存的概念大家都知道,就是保存一份數(shù)據(jù)拷貝。它的特點(diǎn)是速度快,內(nèi)存小, 并且價格昂貴。那么,程序的執(zhí)行過程就變成了:程序在運(yùn)行過程中,會將運(yùn)算需要的數(shù)據(jù)從主 存復(fù)制一份到CPU的高速緩存當(dāng)中。那么CPU進(jìn)行計算時就可以直接從它的高速緩存讀取數(shù)據(jù)和向其中寫入數(shù)據(jù),

4、當(dāng)運(yùn)算結(jié)束之后,再將高速緩存中的數(shù)據(jù)刷新到主存當(dāng)中。之后,這家公司開始設(shè)立中層管理人員,管理人員直接歸CEO領(lǐng)導(dǎo),領(lǐng)導(dǎo)有什 么指示,直接告訴管理人員,然后就可以去做自己的事情了。管理人員負(fù)責(zé)去協(xié) 調(diào)底層員工的工作。因?yàn)楣芾砣藛T是了解手下的人員以及自己負(fù)責(zé)的事情的。所以大多數(shù)時候,公司 的各種決策,通知等,CEO只要和管理人員之間溝通就夠了。而隨著CPU能力的不斷提升,一層緩存就慢慢的無法滿足要求了,就逐漸的衍 生出多級緩存。按照數(shù)據(jù)讀取順序和與CPU結(jié)合的緊密程度,CPU緩存可以分為一級緩存 (匚1),二級緩存(L2),部分高端CPU還具有三級緩存(L3),每一級緩 存中所儲存的全部數(shù)據(jù)都是下

5、一級緩存的一部分。這三種緩存的技術(shù)難度和制造成本是相對遞減的,所以其容量也是相對遞增的。 那么,在有了多級緩存之后,程序的執(zhí)行就變成了:當(dāng)CPU要讀取一個數(shù)據(jù)時, 首先從一級緩存中查找,如果沒有找到再從二級緩存中查找,如果還是沒有就從 三級緩存或內(nèi)存中查找。隨著公司越來越大,老板要管的事情越來越多,公司的管理部門開始改革,開始 出現(xiàn)高層,中層,底層等管理者。一級一級之間逐層管理。單核CPU只含有一套L1,L2,L3緩存;如果CPU含有多個核心,即多核 CPU,則每個核心都含有一套L1 (甚至和L2)緩存,而共享L3 (或者和L2) 緩存。公司也分很多種,有些公司只有一個大Boss,他一個人說了

6、算。但是有些公司 有比如聯(lián)席總經(jīng)理、合伙人等機(jī)制。單核CPU就像一家公司只有一個老板,所有命令都來自于他,那么就只需要一 套管理班底就夠了。多核CPU就像一家公司是由多個合伙人共同創(chuàng)辦的,那么,就需要給每個合伙 人都設(shè)立一套供自己直接領(lǐng)導(dǎo)的高層管理人員,多個合伙人共享使用的是公司的 底層員工。還有的公司,不斷壯大,開始拆分出各個子公司。各個子公司就是多個CPU 了, 互相之前沒有共用的資源?;ゲ挥绊?。下圖為一個單CPU雙核的緩存結(jié)構(gòu):隨著計算機(jī)能力不斷提升,開始支持多線程。那么問題就來了,我們分別來分析 下單線程、多線程在單核CPU、多核CPU中的影響。單線程:CPU核心的緩存只被一個線程訪問

7、。緩存獨(dú)占,不會出現(xiàn)訪問沖突等 問題。單核CPU,多線程:進(jìn)程中的多個線程會同時訪問進(jìn)程中的共享數(shù)據(jù),CPU將 某塊內(nèi)存加載到緩存后,不同線程在訪問相同的物理地址的時候,都會映射到相 同的緩存位置,這樣即使發(fā)生線程的切換,緩存仍然不會失效。但由于任何時刻只能有一個線程在執(zhí)行,因此不會出現(xiàn)緩存訪問沖突。多核CPU,多線程:每個核都至少有一個L1緩存。多個線程訪問進(jìn)程中的某 個共享內(nèi)存,且這多個線程分別在不同的核心上執(zhí)行,則每個核心都會在各自的 Cache中保留一份共享內(nèi)存的緩沖。由于多核是可以并行的,可能會出現(xiàn)多個線程同時寫各自的緩存的情況,而各自 的Cache之間的數(shù)據(jù)就有可能不同。在CPU和

8、主存之間增加緩存,在多線程場景下就可能存在緩存一致性問題,也 就是說,在多核CPU中,每個核的自己的緩存中,關(guān)于同一個數(shù)據(jù)的緩存內(nèi)容 可能不一致。如果這家公司的命令都是串行下發(fā)的話,那么就沒有任何問題。如果這家公司的命令都是并行下發(fā)的話,并且這些命令都是由同一個CEO下發(fā) 的,這種機(jī)制是也沒有什么問題。因?yàn)樗拿顖?zhí)行者只有一套管理體系。如果這家公司的命令都是并行下發(fā)的話,并且這些命令是由多個合伙人下發(fā)的, 這就有問題了。因?yàn)槊總€合伙人只會把命令下達(dá)給自己直屬的管理人員,而多個管理人員管理的 底層員工可能是公用的。比如,合伙人1要辭退員工a,合伙人2要給員工a升職,升職后的話他再 被辭退需要多

9、個合伙人開會決議。兩個合伙人分別把命令下發(fā)給了自己的管理人 員。合伙人1命令下達(dá)后,管理人員a在辭退了員工后,他就知道這個員工被開除 了。而合伙人2的管理人員2這時候在沒得到消息之前,還認(rèn)為員工a是在職的,處理器優(yōu)化和指令重排上面提到在CPU和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。 除了這種情況,還有一種硬件問題也比較重要。那就是為了使處理器內(nèi)部的運(yùn)算 單元能夠被充分利用,處理器可能會對輸入代碼進(jìn)行亂序執(zhí)行處理。這就是處理 器優(yōu)化。除了現(xiàn)在很多流行的處理器會對代碼進(jìn)行優(yōu)化亂序處理,很多編程語言的編譯器 也會有類似的優(yōu)化,比如Java虛擬機(jī)的即時編譯器(JIT)也會做指令重排???/p>

10、想而知,如果任由處理器優(yōu)化和編譯器對指令重排的話,就可能導(dǎo)致各種各樣 的問題。關(guān)于員工組織調(diào)整的情況,如果允許人事部在接到多個命令后進(jìn)行隨意拆分亂序個員工以及這家公司的影響是非常大的。個員工以及這家公司的影響是非常大的。前面說的和硬件有關(guān)的概念你可能聽得有點(diǎn)蒙,還不知道他到底和軟件有啥關(guān)系。 但是關(guān)于并發(fā)編程的問題你應(yīng)該有所了解了,比如原子性問題,可見性問題和有 序性問題。其實(shí),原子性問題,可見性問題和有序性問題是人們抽象定義出來的。而這個抽 象的底層問題就是前面提到的緩存一致性問題、處理器優(yōu)化問題和指令重排問題 等。這里簡單回顧下這三個問題,我們說,并發(fā)編程,為了保證數(shù)據(jù)的安全,需要滿 足以

11、下三個特性:原子性,是指在一個操作中,CPU不可以在中途暫停然后再調(diào)度,即不被 中斷操作,要不執(zhí)行完成,要不就不執(zhí)行??梢娦裕侵府?dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的 值,其他線程能夠立即看得到修改的值。有序性,即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。有沒有發(fā)現(xiàn),緩存一致性問題其實(shí)就是可見性問題。而處理器優(yōu)化是可以導(dǎo)致原 子性問題的。指令重排即會導(dǎo)致有序性問題。所以,后文將不再提起硬件層面的那些概念,而是直接使用大家熟悉的原子性、樣么冥為存模型前面提到的,緩存一致性問題、處理器優(yōu)化的指令重排問題是硬件的不斷升級導(dǎo) 致的。那么,有沒有什么機(jī)制可以很好的解決上面的這些問題呢?最簡單

12、直接的做法就是廢除處理器和處理器的優(yōu)化技術(shù)、廢除CPU緩存,讓 CPU直接和主存交互。但是,這么做雖然可以保證多線程下的并發(fā)問題。但是,這就有點(diǎn)因噎廢食了。 所以,為了保證并發(fā)編程中可以滿足原子性、可見性及有序性。有一個重要的概 念,那就是一一內(nèi)存模型。為了保證共享內(nèi)存的正確性(可見性、有序性、原子性),內(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)存訪問問題,保證 了并發(fā)場景下的一致性、原子性和有序性。內(nèi)

13、存模型解決并發(fā)問題主要采用兩種方式:限制處理器優(yōu)化使用內(nèi)存屏障什文么不是入底J原理來展內(nèi)介紹學(xué)模感型趣的朋友可以自行學(xué)習(xí)。前面介紹了計算機(jī)內(nèi)存模型,這是解決多線程場景下并發(fā)問題的一個重要規(guī)范。 那么具體的實(shí)現(xiàn)是如何的呢?不同的編程語言,在實(shí)現(xiàn)上可能有所不同。我們知道,Java程序是需要運(yùn)行在Java虛擬機(jī)上面的,Java內(nèi)存模型(Java Memory Model,JMM)就是一種符合內(nèi)存模型規(guī)范的,屏蔽了各種硬件和操作 系統(tǒng)的訪問差異的,保證了 Java程序在各種平臺下對內(nèi)存的訪問都能保證效果 一致的機(jī)制及規(guī)范。提到Java內(nèi)存模型,一般指的是JDK 5開始使用的新內(nèi)存模型,主要由 JSR-

14、133: JavaTM Memory Model and Thread Specification 描述。感興趣的可以參看下這份PDF文檔: HYPERLINK /pugh/java/memoryModel/jsr133.pdf /pugh/java/memoryModel/jsr133.pdfJava內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程還有自己的工作 內(nèi)存。線程的工作內(nèi)存中保存了該線程中用到的變量的主內(nèi)存副本拷貝,線程對變量的 所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需 要自己的工作內(nèi)存和主存之間

15、進(jìn)行數(shù)據(jù)同步進(jìn)行。而JMM就作用于工作內(nèi)存和主存之間數(shù)據(jù)同步過程。它規(guī)定了如何做數(shù)據(jù)同 步以及什么時候做數(shù)據(jù)同步。這里面提到的主內(nèi)存和工作內(nèi)存,讀者可以簡單的類比成計算機(jī)內(nèi)存模型中的主 存和緩存的概念。特別需要注意的是,主內(nèi)存和工作內(nèi)存與JVM內(nèi)存結(jié)構(gòu)中的Java堆、棧、方 法區(qū)等并不是同一個層次的內(nèi)存劃分,無法直接類比。深入理解Java虛擬機(jī)中認(rèn)為:如果一定要勉強(qiáng)對應(yīng)起來的話,從變量、主 內(nèi)存、工作內(nèi)存的定義來看,主內(nèi)存主要對應(yīng)于Java堆中的對象實(shí)例數(shù)據(jù)部分。 而工作內(nèi)存則對應(yīng)于虛擬機(jī)棧中的部分區(qū)域。所以,再來總結(jié)下,JMM是一種規(guī)范,是解決由于多線程通過共享內(nèi)存進(jìn)行通 信時,存在的本地內(nèi)

16、存數(shù)據(jù)不一致、編譯器會對代碼指令重排序、處理器會對代 碼亂序執(zhí)行等帶來的問題。費(fèi)囂證芮舞障地箭熨妒和有序性。了解Java多線程的朋友都知道,在Java中提供了一系列和并發(fā)處理相關(guān)的關(guān) 鍵字,比如 Volatile、Synchronized Final、Concurren 包等。其實(shí)這些就是Java內(nèi)存模型封裝了底層的實(shí)現(xiàn)后提供給程序員使用的一些關(guān) 鍵字。在開發(fā)多線程的代碼的時候,我們可以直接使用Synchronized等關(guān)鍵字來控制 并發(fā),這樣就不需要關(guān)心底層的編譯器優(yōu)化、緩存一致性等問題。所以,Java內(nèi)存模型,除了定義了一套規(guī)范,還提供了一系列原語,封裝了底 層實(shí)現(xiàn)后,供開發(fā)者直接使用。我

17、們前面提到,并發(fā)編程要解決原子性、有序性和一致性的問題。下面我們就再 來看下,在Java中,分別使用什么方式來保證。原子性在Java中,為了保證原子性,提供了兩個高級的字節(jié)碼指令 Monitorenter和 Monitorexit o在Synchronized的實(shí)現(xiàn)原理文章中,介紹過,這兩個字節(jié)碼,在Java中對應(yīng) 的關(guān)鍵字就是Synchronized。因此,在Java中可以使用Synchronized來保證方法和代碼塊內(nèi)的操作是原子 性的。可見性Java內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi) 存刷新變量值的這種依賴主內(nèi)存作為傳遞媒介的方式來實(shí)現(xiàn)的。Java中的Volatile關(guān)鍵字

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論