學(xué)習(xí)筆記20170601">【PMP】學(xué)習(xí)筆記20170601
711
2022-05-30
你好我是辰兮,很高興與你分享近期學(xué)習(xí)總結(jié),上一篇整理完Java虛擬機(jī)內(nèi)存結(jié)構(gòu),為了對比學(xué)習(xí),本篇是對Java內(nèi)存模型的介紹,整理完收獲頗多,分享出來,希望對你們理解學(xué)習(xí)有幫助。
Java內(nèi)存結(jié)構(gòu)參考:Java虛擬機(jī)的內(nèi)存結(jié)構(gòu)
文章目錄
一、簡述Java內(nèi)存模型
二、CPU和緩存一致性
三、Java內(nèi)存模型的實(shí)現(xiàn)
一、簡述Java內(nèi)存模型
Java內(nèi)存模型(Java Memory Model,JMM)是java虛擬機(jī)規(guī)范定義的,用來屏蔽掉java程序在各種不同的硬件和操作系統(tǒng)對內(nèi)存的訪問的差異,這樣就可以實(shí)現(xiàn)java程序在各種不同的平臺上都能達(dá)到內(nèi)存訪問的一致性。
可以避免像c++等直接使用物理硬件和操作系統(tǒng)的內(nèi)存模型在不同操作系統(tǒng)和硬件平臺下表現(xiàn)不同,比如有些c/c++程序可能在windows平臺運(yùn)行正常,而在linux平臺卻運(yùn)行有問題。
ps:
JVM中的堆啊、棧啊、方法區(qū)什么的,是Java虛擬機(jī)的內(nèi)存結(jié)構(gòu)。內(nèi)存模型概念如上,千千萬萬不要再搞混淆了。
二、CPU和緩存一致性
計(jì)算機(jī)在執(zhí)行程序的時候,每條指令都是在CPU中執(zhí)行的,而執(zhí)行的時候,又免不了要和數(shù)據(jù)打交道。而計(jì)算機(jī)上面的數(shù)據(jù),是存放在主存當(dāng)中的,也就是計(jì)算機(jī)的物理內(nèi)存。
隨著CPU技術(shù)的發(fā)展,
CPU的執(zhí)行速度越來越快
。而由于
內(nèi)存的技術(shù)并沒有太大的變化
,所以從內(nèi)存中讀取和寫入數(shù)據(jù)的過程和CPU的執(zhí)行速度比起來差距就會越來越大,這就導(dǎo)致CPU每次操作內(nèi)存都要耗費(fèi)很多等待時間。
不能因?yàn)閮?nèi)存的讀寫速度慢,就不發(fā)展CPU(中央處理器)技術(shù)了吧。
人們想到了用緩存來解決這個問題
,就是在CPU和內(nèi)存之間增加高速緩存。緩存的概念大家都知道,就是保存一份數(shù)據(jù)拷貝。他的特點(diǎn)是速度快,內(nèi)存小,并且昂貴。
當(dāng)程序在運(yùn)行過程中,會將運(yùn)算需要的數(shù)據(jù)從主存復(fù)制一份到CPU的高速緩存當(dāng)中,那么CPU進(jìn)行計(jì)算時就可以直接從它的高速緩存讀取數(shù)據(jù)和向其中寫入數(shù)據(jù),當(dāng)運(yùn)算結(jié)束之后,再將高速緩存中的數(shù)據(jù)刷新到主存當(dāng)中。
Java內(nèi)存模型的引入
Java內(nèi)存模型規(guī)定了①所有的變量都存儲在主內(nèi)存中,②每條線程還有自己的工作內(nèi)存,③線程的工作內(nèi)存中保存了該線程中是用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。
不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步進(jìn)行。
JMM就作用于工作內(nèi)存和主存之間數(shù)據(jù)同步過程。
他規(guī)定了如何做數(shù)據(jù)同步以及什么時候做數(shù)據(jù)同步。
注意:
主內(nèi)存和工作內(nèi)存與JVM內(nèi)存結(jié)構(gòu)中的Java堆、棧、方法區(qū)等并不是同一個層次的內(nèi)存劃分,無法直接類比。
小結(jié):
JMM是一種規(guī)范,目的是解決由于多線程通過共享內(nèi)存進(jìn)行通信時,存在的本地內(nèi)存數(shù)據(jù)不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執(zhí)行等帶來的問題。
目的是保證并發(fā)編程場景中的原子性、可見性和有序性。
三、Java內(nèi)存模型的實(shí)現(xiàn)
了解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ā)者直接使用。
重點(diǎn)要介紹的就是,我們前面提到,并發(fā)編程要解決原子性、有序性和一致性的問題,我們就再來看下,在Java中,分別使用什么方式來保證。
1、原子性
在Java中,為了保證原子性,提供了兩個高級的字節(jié)碼指令monitorenter和monitorexit。在synchronized的實(shí)現(xiàn)原理文章中,介紹過,這兩個字節(jié)碼,在Java中對應(yīng)的關(guān)鍵字就是synchronized。
因此,
在Java中可以使用synchronized來保證方法和代碼塊內(nèi)的操作是原子性的。
2、可見性
Java內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存刷新變量值的這種依賴主內(nèi)存作為傳遞媒介的方式來實(shí)現(xiàn)的。
Java中的volatile關(guān)鍵字提供了一個功能,
那就是被其修飾的變量在被修改后可以立即同步到主內(nèi)存,被其修飾的變量在每次是用之前都從主內(nèi)存刷新
。因此,可以使用volatile來保證多線程操作時變量的可見性。
除了volatile,Java中的synchronized和final兩個關(guān)鍵字也可以實(shí)現(xiàn)可見性。只不過實(shí)現(xiàn)方式不同,這里不再展開了。
3、有序性
在Java中,可以使用
synchronized和volatile
來保證多線程之間操作的有序性。
實(shí)現(xiàn)方式有所區(qū)別:volatile關(guān)鍵字會禁止指令重排。synchronized關(guān)鍵字保證同一時刻只允許一條線程操作。
簡單的介紹完了Java并發(fā)編程中解決原子性、可見性以及有序性可以使用的關(guān)鍵字。大家發(fā)現(xiàn)好像synchronized關(guān)鍵字是萬能的,他可以同時滿足以上三種特性,這其實(shí)也是很多人濫用synchronized的原因。
但是synchronized是比較影響性能的,雖然編譯器提供了很多鎖優(yōu)化技術(shù),但是也不建議過度使用。
大家可以參考《深入理解Java虛擬機(jī)》
The best investment is to invest in yourself
2020.05.21 記錄辰兮的第68篇博客
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。