java內存模型 JMM
721
2022-05-29
并發編程模型的兩個關鍵問題
線程間如何通信?即:線程之間以何種機制來交換信息
線程間如何同步?即:線程以何種機制來控制不同線程間操作發生的相對順序
有兩種并發模型可以解決這兩個問題:
消息傳遞并發模型
共享內存并發模型
既然堆是共享的,為什么在堆中會有內存不可見問題?
這是因為現代計算機為了高效,往往會在高速緩存區中緩存共享變量,因為cpu訪問緩存區比訪問內存要快得多。
線程之間的共享變量存在主內存中,每個線程都有一個私有的本地內存,存儲了該線程以讀、寫共享變量的副本。本地內存是Java內存模型的一個抽象概念,并不真實存在。它涵蓋了緩存、寫緩沖區、寄存器等。
Java線程之間的通信由Java內存模型(簡稱JMM)控制,從抽象的角度來說,JMM定義了線程和主內存之間的抽象關系。JMM的抽象示意圖如圖所示:
所有的共享變量都存在主內存中。
每個線程都保存了一份該線程使用到的共享變量的副本。
如果線程A與線程B之間要通信的話,必須經歷下面2個步驟:
線程A將本地內存A中更新過的共享變量刷新到主內存中去。
線程B到主內存中去讀取線程A之前已經更新過的共享變量。
所以,線程A無法直接訪問線程B的工作內存,線程間通信必須經過主內存。
注意,根據JMM的規定,線程對共享變量的所有操作都必須在自己的本地內存中進行,不能直接從主內存中讀取。
所以線程B并不是直接去主內存中讀取共享變量的值,而是先在本地內存B中找到這個共享變量,發現這個共享變量已經被更新了,然后本地內存B去主內存中讀取這個共享變量的新值,并拷貝到本地內存B中,最后線程B再讀取本地內存B中的新值。
那么怎么知道這個共享變量的被其他線程更新了呢?這就是JMM的功勞了,也是JMM存在的必要性之一。JMM通過控制主內存與每個線程的本地內存之間的交互,來提供內存可見性保證。
Java中的volatile關鍵字可以保證多線程操作共享變量的可見性以及禁止指令重排序,synchronized關鍵字不僅保證可見性,同時也保證了原子性(互斥性)。在更底層,JMM通過內存屏障來實現內存的可見性以及禁止重排序。為了程序員的方便理解,提出了happens-before,它更加的簡單易懂,從而避免了程序員為了理解內存可見性而去學習復雜的重排序規則以及這些規則的具體實現方法。這里涉及到的所有內容后面都會有專門的章節介紹。
JMM與Java內存區域劃分的區別與聯系
上面兩小節分別提到了JMM和Java運行時內存區域的劃分,這兩者既有差別又有聯系:
區別
兩者是不同的概念層次。JMM是抽象的,他是用來描述一組規則,通過這個規則來控制各個變量的訪問方式,圍繞原子性、有序性、可見性等展開的。而Java運行時內存的劃分是具體的,是JVM運行Java程序時,必要的內存劃分。
聯系
都存在私有數據區域和共享數據區域。一般來說,JMM中的主內存屬于共享數據區域,他是包含了堆和方法區;同樣,JMM中的本地內存屬于私有數據區域,包含了程序計數器、本地方法棧、虛擬機棧。
實際上,他們表達的是同一種含義,這里不做區分。
Java 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。