Java內存模型

Java內存模型

概述

理解Java內存模型的概念及原理,對於使用Java語言開發的工做者來講相當重要,對實際項目開發也有促進做用,也時常做爲面試題出如今各大互聯網公司的面試環節。本篇簡要介紹了Java內存模型關鍵部份內容,結合原理圖具體說明。深刻理解Java內存模型細節推薦閱讀《深刻理解Java虛擬機》第12章,本文主要原理內容說明也來自於此。面試

硬件的效率與一致性

  • 主內存

簡單來講是一片共享的內存區域。計算機的處理器老是併發的執行多任務,在執行任務期間處理器會與主內存進行交互,如讀取數據、存儲運算結果等。緩存

  • 高速緩存

內存與處理器之間的緩衝。因爲計算機的存儲設備(IO操做)與處理器的運算速度有幾個數量級的差距,因此現代計算機系統引入一層讀寫速度儘量接近處理器運算速度的高速緩存(Cache)來做爲內存與處理器之間的緩衝:將運算須要使用到的數據複製到緩存中,讓運算速度能快速進行,當運算結束背後再從緩存同步回主內存中,這樣處理器舊無需等待緩慢的內存讀寫了。微信

  • 緩存一致性協議

當多個處理器的運算任務都涉及同一主內存時,可能出現各自的緩存數據不一致,而最終同步到主內存以哪一個緩存數據爲準。緩存一致性協議主要是約定了讀寫數據操做的協議,根據協議最終能保證數據一致性。併發

處理器、高速緩存、主內存間的交互關係: 優化

Java內存模型

Java內存模型規定了全部的變量都存儲在主內存(Main Memory)中(此處的主內存與介紹物理硬件時的主內存名字同樣,二者也能夠相互類比,但此處僅是虛擬機內存的一部分)。每條線程還有本身的工做內存(Working Memory,可與前面講的處理器高速緩存類比),線程的工做內存保存了該線程使用到的變量的主內存拷貝副本,線程對變量的全部操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成,線程、主內存、工做內存三者的關係圖: 線程

  • note:假設線程中訪問一個10M的對象,也會把10M的內存複製一份拷貝出來嗎?

事實上並不會如此,這個對象的引用、對象中某個在線程訪問到的字段是有可能存在拷貝的,但不會有虛擬機實現成把整個對象拷貝一次。cdn

內存間交互操做

Java內存模型定義的8種操做:(均是原子的吧,不可再分的,可是對於double和long類型的變量來講,load、store、read和write操做在某些平臺上容許有例外)對象

  • lock(鎖定):做用於主內存的變量,把一個變量標識爲一條線程獨佔狀態。
  • unlock(解鎖):做用於主內存變量,把一個處於鎖定狀態的變量釋放出來,釋放後的變量才能夠被其餘線程鎖定。
  • read(讀取):做用於主內存變量,把一個變量值從主內存傳輸到線程的工做內存中,以便隨後的load動做使用。
  • load(載入):做用於工做內存的變量,它把經過read操做從主內存中獲得的變量值放入工做內存的變量副本中。
  • use(使用):做用於工做內存的變量,把工做內存中的一個變量值傳遞給執行引擎,每當虛擬機遇到一個須要使用變量的值的字節碼指令時將會執行這個操做。
  • assign(賦值):做用於工做內存的變量,它把一個從執行引擎接收到的值賦值給工做內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操做。
  • store(存儲):做用於工做內存的變量,把工做內存中的一個變量的值傳送到主內存中,以便隨後的write的操做使用。
  • write(寫入):做用於主內存的變量,它把經過store操做從工做內存中獲得的變量的值放入主內存的變量中。

經過上面的內存交互規則,若是要把一個變量從主內存複製到工做內存, 就須要順序的執行read和load操做,而若是要把一個變量從工做內存同步回主內存,則須要順序執行store和write操做,注意是順序執行,而不是連續執行,也就是兩個操做之間能夠插入其餘操做。除此以外,Java內存模型對這8中操做進行操做的其餘約束:blog

  • 不容許read和load、store和write操做之一單獨出現。
  • 不容許線程丟棄它的最近的assign操做,即變量在工做內存中改變以後,必須同步回主內存。
  • 不容許線程把沒有通過assign操做的變量,同步回主內存。
  • 一個新的變量只能在主內存中誕生,即對一個變量進行use、store操做以前,必須先執行過load、assign操做。
  • 一個變量在同一時刻只能被一條線程執行lock操做,一旦lock成功,能夠被同一線程重複lock屢次,屢次執行lock以後,只有執行相同次數的unlock操做,變量纔會被解鎖。
  • 對一個變量執行lock操做,將會清空工做內存中該變量的值,因此在執行引擎使用這個變量前,須要從新執行load或assign操做對其進行初始化。
  • 對一個變量執行unlock操做以前,必須先把該變量同步回主內存(執行store、write操做)。
  • 若是一個變量事先沒有被lock操做鎖定,那就不容許對它執行unlock操做,也不容許unlock一個被其餘線程lock的變量。

關於內存交互操做的流程簡圖:排序

volatile變量

volatile變量具有兩種特性:

  • 保證變量對全部線程的可見性,這裏的「可見性」是指當一個線程修改了這個變量值,新值對於其餘線程來講是能夠當即獲得的。

  • 禁止指令重排序優化

關於volatile變量,除了理解可見性以及指令重排,理解原子性有序性以及volatile對於long和dounble型變量的特殊規則,先行發生原則也相當重要,因爲篇幅關係本文再也不深刻介紹。

總結

關於Java內存模型,能夠先從硬件處理器執行背景進行介紹,結合硬件的效率與一致性問題,延伸到虛擬機的Java內存模型約定的概念,如主內存工做內存,內存間交互操做進行說明,更進一步對volatile關鍵字的理解,可見性,原子性,有序性,指令重排,先行發生等等層層遞進。

Alt

掃一掃 關注個人微信訂閱號號
相關文章
相關標籤/搜索