「Java多線程系列」真正的底層核心:Java內存模型JMM

本篇屬於【Java多線程系列】文章第一章【基本概念與底層原理】的第三小節內容,作為理解Java線程運行原理的基礎知識,我們會對Java的內存模型JMM(Java Memory Model)進行詳細講解。

  • 什麼是Java內存模型

在面試過程中,我們經常會被問到一個問題,請你講解一下內存模型,這通常指的是JVM的內存分區(由堆、棧、程序計數器、元空間、本地方法棧組成),而本篇所講的Java內存模型指的是一種虛擬機以及內存訪問的規範

"一次編寫,到處運行"(Write once, run anywhere)是Java程序設計語言的跨平臺特性的口號。 理想中這意味著Java可以在任何設備上開發,編譯成一段標準的字節碼並且可以在任何安裝有Java虛擬機(JVM)的設備上運行,在芯片、設備和軟件包中安裝Java已經成為一種工業實踐的標準。

我們知道,Java程序是需要運行在Java虛擬機JVM上面的,因此Java的內存模型主要目的是為了屏蔽各種硬件和操作系統的訪問差異的,保證了Java程序在各種平臺下對內存的訪問都能保證效果一致的機制及規範

JMM規範了Java虛擬機與計算機內存是如何協同工作的:規定了一個線程如何和何時可以看到由其他線程修改過後的共享變量的值,以及在必須時如何同步的訪問共享變量。

  • 主存 VS 工作內存

JMM定義了線程和主內存之間的抽象關係:線程之間的共享變量存儲在主內存(Main Memory)中,每個線程都有一個私有的本地內存(Local Memory),本地內存中存儲了該線程以讀/寫共享變量的副本。

「Java多線程系列」真正的底層核心:Java內存模型JMM

線程的工作內存中保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存中的變量。

不同的線程之間也無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成。

  • 八大原子操作

JMM定義了八大原子操作,以此來實現主存與工作內存間的數據交互,並確保每一種操作是原子性的,也就是不可分割的:

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

如果需要把一個變量從主內存複製到工作內存,那就要順序地執行read和load操作,如果要把變量從工作內存同步回主內存,就要順序地執行store和write操作。

注意,Java內存模型只要求上述兩個操作必須按順序執行,而沒有保證是連續執行。也就是說read與load之間、store與write之間是可插入其他指令的,如對主內存中的變量a、b進行訪問時,一種可能出現順序是read a、read b、load b、load a。

  • Java內存模型的實現

瞭解Java多線程的朋友都知道,在Java中提供了一系列和併發處理相關的關鍵字,比如volatile、synchronized、final、concurren包等。其實這些就是Java內存模型封裝了底層的實現後提供給程序員使用的一些關鍵字。

在開發多線程的代碼的時候,我們可以直接使用synchronized等關鍵字來控制併發,從來就不需要關心底層的編譯器優化、緩存一致性等問題。所以,Java內存模型,除了定義了一套規範,還提供了一系列原語,封裝了底層實現後,供開發者直接使用。

對於這些具體的實現方式包括一些工具類,我們將在接下來的文章進行詳細的講解。


分享到:


相關文章: