理解 JVM:Java 內存模型之(基礎)

併發編程

併發:指同時發生,在操做系統中,是指一個時間段中有幾個程序都處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任一個時刻點上只有一個程序在處理機上運行。java

  • 在併發編程中須要處理兩個關鍵問題:線程之間如何通訊及線程之間如何同步。
    1. 通訊:是指線程之間如何交換信息。在命令式編程中,線程之間的通訊機制有兩種:共享內存和消息傳遞。在共享內存的併發模型裏,線程之間共享程序的公共狀態,線程之間經過寫-讀內存中的公共狀態來隱式進行通訊。在消息傳遞的併發模型裏,線程之間沒有公共狀態,線程之間必須經過明確的發送消息來顯式進行通訊。
    2. 同步:是指程序用於控制不一樣線程之間操做發生相對順序的機制。在共享內存併發模型裏,同步是顯式進行的。程序員必須顯式指定某個方法或某段代碼須要在線程之間互斥執行。在消息傳遞的併發模型裏,因爲消息的發送必須在消息的接收以前,所以同步是隱式進行的。在 Java 中,能夠經過 volatile,synchronized, 鎖等方式實現同步。

 

Java 內存模型

Java的併發採用的是共享內存模型,Java線程之間的通訊老是隱式進行,整個通訊過程對程序員徹底透明。Java 虛擬機規範中試圖定義一種 Java 內存模型(Java Model Memory,JMM)來屏蔽掉各類硬件和操做系統的內存訪問差別,以實現讓 Java 程序在各類平臺下都能達到一致的內存訪問效果。程序員

主內存與工做內存

Java 內存模型的主要目標是定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存中取出變量這樣的底層細節。此處的變量(Variables)與 Java 編程中所說的變量有所區別,它包括了實例字段、靜態字段和構成數組對象的元素,但不包括局部變量與方法參數,由於後者是線程私有的,不會被共享,天然不會存在競爭問題。編程

Java 內存模型規定了全部的變量都存儲在主內存(Main Memory)中,每條線程還有本身的工做內存(Working Memory),線程的工做內存中保存被該線程使用到的變量在主內存中變量的副本,線程對變量的多有操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量的值得傳遞均須要經過主內存來完成,線程、主內存、工做內存三者的交互關係如圖所示:數組

java-memory-model.jpg

這裏所講的主內存、工做內存與 Java 內存區域中的 Java 堆、棧、方法區等並非同一個層次的內存劃分,這二者基本上沒有關係,若是二者必定要勉強對應起來,那從變量、主內存、工做內存的定義來看,主內存主要對應於 Java 堆中的對象實例數據部分,而工做內存則對應於虛擬機棧中的部分區域。從更低層次上說,主內存就直接對應於物理硬件內存,而爲了獲取更好的運行速度,虛擬機可能會讓工做內存優先存儲於寄存器和高速緩存中,由於程序運行時注意訪問讀寫的是工做內存。緩存

內存間交互操做

Java 內存模型定義了 8 種操做來完成主內存與工做內存之間具體的交互協議,即一個變量如何從主內存拷貝到工做內存、如何從工做內存同步會主內存的。虛擬機是現實必需要保證下面說起的每一種操做都是原子的、不可再分的。併發

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

Java 內存模型海規定了在執行上述 8 種基本操做是必須知足以下規則:spa

  • 若是把一個變量從主內存複製到工做內存,那就要順序地執行 read 和 load 操做。
  • 若是把一個變量從工做內存同步回主內存,那就要順序地執行 store 和 write 操做。
  • 不容許 read 和 load、store 和 write 操做之一單獨出現,即不容許一個變量從主內存讀取了但工做內存不接受,或者從工做內存發起回寫了但主內存不接受的狀況出現。
  • 不容許一個線程丟棄它的最近的 assign 操做,即變量在工做內存中改變了以後必須把該變化同步回主內存。
  • 不容許一個線程無緣由地(沒有發生任何 assign 操做)把數據從線程的工做內存同步回主內存中。
  • 一個新的變量只能在主內存中「誕生」,不容許在工做內存中直接使用一個未被初始化(load 和 assign)的變量,換句話說,就是對一個變量實施 use、store 操做以前,必須先執行過了 assign 和 load 操做。
  • 一個變量在同一個時刻只容許一個線程對其進行 lock 操做,但 lock 操做能夠被同一條線程重複執行屢次,屢次執行 lock 後,只有執行相同次數的 unlock 操做,變量纔會被解鎖。
  • 若是對一個變量執行 lock 操做,那將會清空工做內存中此變量的值,在執行引擎使用這個變量前,須要從新執行 load 或 assign 操做初始化變量的值。
  • 對一個變量執行 unlock 操做以前,必須先把此變量同步回主內存中(執行 store、write 操做)。

參考文獻

 

歡迎掃一掃關注 程序猿pdh 公衆號!操作系統

相關文章
相關標籤/搜索