學(xué)習(xí)Java內(nèi)存模型JMM心得
有時(shí)候編譯器、處理器的優(yōu)化會(huì)導(dǎo)致runtime與我們?cè)O(shè)想的不一樣,為此Java對(duì)編譯器和處理器做了一些限制,JAVA內(nèi)存模型(JMM)將這些抽象出來(lái),這樣編寫(xiě)代碼時(shí)就無(wú)需考慮那么多底層細(xì)節(jié),并保證“只要遵循JMM的規(guī)則編寫(xiě)程序,其運(yùn)行結(jié)果一定是正確的”。
JMM的抽象結(jié)構(gòu)
在Java中,所有的實(shí)例、靜態(tài)變量存儲(chǔ)在堆內(nèi)存中,堆內(nèi)存是可以在線程間共享的,這部分也稱(chēng)為共享變量。而局部變量、方法定義參數(shù)、異常處理參數(shù)是在棧中的,棧內(nèi)存不在線程間共享。
而由于編譯器、處理器的優(yōu)化,會(huì)導(dǎo)致共享變量出現(xiàn)可見(jiàn)性問(wèn)題,像在多核處理器中(multi-processor),線程可以在不同的處理器上執(zhí)行,而處理器之間緩存不一致,會(huì)使共享變量出現(xiàn)可見(jiàn)性問(wèn)題,有可能兩個(gè)線程看到同一個(gè)變量不同值。
JMM將這些硬件做的優(yōu)化抽象成每個(gè)線程都有一個(gè)本地內(nèi)存。需要讀寫(xiě)共享變量時(shí),從主內(nèi)存中拷貝一份到本地內(nèi)存。當(dāng)寫(xiě)共享變量時(shí),先寫(xiě)到本地內(nèi)存中去,在將來(lái)某個(gè)時(shí)間再刷新到主內(nèi)存中。當(dāng)再次讀共享變量時(shí),則只會(huì)從本地內(nèi)存中讀取。
這樣線程間通訊就需要經(jīng)過(guò)兩步:
寫(xiě)線程:刷新本地內(nèi)存到主內(nèi)存中去讀線程:從主內(nèi)存讀取更新后的值
這樣在寫(xiě)-讀之間就有一個(gè)延遲:本地內(nèi)存什么時(shí)候刷新到主內(nèi)存中去?導(dǎo)致可見(jiàn)性問(wèn)題,不同線程可能看到的共享變量不一樣。
happens-before
從字面上看happens-before的意思是“發(fā)生在此之前”。這是java對(duì)程序執(zhí)行順序制定的規(guī)則,實(shí)現(xiàn)同步必須遵循該規(guī)則。這樣程序員只需要寫(xiě)出正確的同步程序,happens-before保證運(yùn)行結(jié)果不會(huì)錯(cuò)。
A happens-before B,不僅僅表示A在B之前執(zhí)行,還意味著A的執(zhí)行結(jié)果對(duì)B可見(jiàn),這保證了可見(jiàn)性。
A happens-before B,A也不一定要在B之前執(zhí)行,如果AB交替,執(zhí)行結(jié)果任然正確,則允許編譯器、處理器進(jìn)行優(yōu)化重排序。所以只要程序結(jié)果正確,編譯器、處理器怎么優(yōu)化,怎么重排序都沒(méi)問(wèn)題,都是好的。
happens-before規(guī)則
程序順序規(guī)則:在一個(gè)線程中,前面的操作happens-before后面的操作鎖規(guī)則:對(duì)同一個(gè)鎖,解鎖happens-before加鎖 volatile域規(guī)則:寫(xiě)volatile變量,happens-before后面任意一個(gè)讀這個(gè)volatile變量的操作傳遞性:A happens-before B,B happens-before C,則A happens-before C start()規(guī)則:如果線程A執(zhí)行ThreadB.start() 那么ThreadB.start() happens-before 線程B中任何操作 join()規(guī)則:如果線程A執(zhí)行ThreadB.join(),那么線程B中的所有操作happens-before ThreadB.join()
下面這個(gè)示例有助于理解happens-before
double pi = 3.14; //A double r = 1.0; //B double area = pi * r *r; //C
這里有三個(gè)happens-before關(guān)系,規(guī)則1、2是程序順序規(guī)則,規(guī)則3是傳遞性規(guī)則推導(dǎo)出來(lái)的:
A happens-before B B happens-before C A happens-before C
C依賴(lài)于A、B,但是A和B誰(shuí)也不依賴(lài)。所以即使A和B重排序,執(zhí)行結(jié)果也不會(huì)發(fā)生變化,這種重排序,JMM是運(yùn)行的。
下面兩種執(zhí)行順序的結(jié)果都是正確的。
以上就是我們給大家整理的關(guān)于Java內(nèi)存模型JMM學(xué)習(xí)心得的全部?jī)?nèi)容,更多問(wèn)題大家可以在下方留言討論,感謝你對(duì)腳本之家的支持。
- 淺析Java內(nèi)存模型與垃圾回收
- Java 高并發(fā)三:Java內(nèi)存模型和線程安全詳解
- 在Java內(nèi)存模型中測(cè)試并發(fā)程序代碼
- Java8內(nèi)存模型PermGen Metaspace實(shí)例解析
- Java內(nèi)存模型JMM詳解
- Java內(nèi)存模型與JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)的區(qū)別詳解
- Java內(nèi)存區(qū)域和內(nèi)存模型講解
- Java內(nèi)存模型之happens-before概念詳解
- Java內(nèi)存模型(JMM)及happens-before原理
- 細(xì)談java同步之JMM(Java Memory Model)
- JAVA內(nèi)存模型(JMM)詳解
相關(guān)文章
SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫(kù)連接池操作
這篇文章主要介紹了SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫(kù)連接池操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09SpringBoot引入Thymeleaf的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot引入Thymeleaf的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04springboot實(shí)現(xiàn)簡(jiǎn)單的消息對(duì)話的示例代碼
本文主要介紹了springboot實(shí)現(xiàn)簡(jiǎn)單的消息對(duì)話的示例代碼,可以使用WebSocket技術(shù),WebSocket是一種在客戶(hù)端和服務(wù)器之間提供實(shí)時(shí)雙向通信的協(xié)議,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09Java并發(fā)底層實(shí)現(xiàn)原理學(xué)習(xí)心得
本片文章是學(xué)習(xí)Java并發(fā)底層實(shí)現(xiàn)原理的一篇知識(shí)心得,對(duì)大家學(xué)習(xí)這個(gè)方便的知識(shí)很有幫助,一起參考下。2018-01-01Eclipse中引入com.sun.image.codec.jpeg包報(bào)錯(cuò)的完美解決辦法
Java開(kāi)發(fā)中對(duì)圖片的操作需要引入 com.sun.image.codec.jpeg,但有時(shí)引入這個(gè)包會(huì)報(bào)錯(cuò),利用下面的操作可以完成解決這個(gè)問(wèn)題2018-02-02Java 中的HashMap詳解和使用示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java 中的HashMap詳解和使用示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理,需要的朋友可以參考下2017-05-05spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理
這篇文章主要介紹了spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02