循序漸進--理解什麼是Java內存模型

導讀:近期筆者在閱讀《深入理解Java虛擬機:JVM高級特性與最佳實現(第3版)》,書中提到關於Java內存模型的知識點,但是看完之後還是感覺有些模糊,便查閱一些其他相關資料。本文是筆者經過對知識理解和整理後的一個總結,希望能夠幫助朋友們對Java內存模型有更加清晰的認識,對於文章內容有其他想法或意見建議等,歡迎提出共同討論共同進步。下面將從以下三點展開討論:

  • 內存模型的由來
  • 內存模型的定義
  • Java內存模型及實現

內存模型的由來

1、計算機在執行程序的時候每條指令都是由CPU來執行的。而CPU在執行的時候為了獲取數據,所以難免與主存打交道。

循序漸進--理解什麼是Java內存模型

2、隨著CPU技術的發展其執行越來越高速度,越來也快,同時因內存技術發展比較緩慢,性能沒有太大的變化,所以導致出現CPU每次操作內存都需要耗費一定的等待時間。

循序漸進--理解什麼是Java內存模型

3、為在保證CPU技術發展同時優化解決這一問題,人們後來想出來了一個方案,就是在CPU和內存之間增加高速緩衝存儲器(Cache)。

高速緩衝存儲器是存在於主存與CPU之間的一級存儲器, 由靜態存儲芯片(SRAM)組成,容量比較小但速度比主存高得多, 接近於CPU的速度。在計算機存儲系統的層次結構中,是介於中央處理器和主存儲器之間的高速小容量存儲器。它和主存儲器一起構成一級的存儲器。高速緩衝存儲器和主存儲器之間信息的調度和傳送是由硬件自動進行的。

循序漸進--理解什麼是Java內存模型

因為Cache速度接近於CPU的速度且CPU每次操作主存前都會先訪問Cache,所以通過增加Cache後當便達到了優化的效果。

4、隨著CPU的升級,一層緩存慢慢地無法滿足要求,因此逐漸地衍生出多級緩存。每一級緩存中所儲存的全部數據都是下一級緩存的一部分。而CPU讀取數據也演變為:當CPU要讀取一個數據時,首先從一級緩存中查找,如果沒有找到再從二級緩存中查找,如果還是沒有就從下一級緩存查直到訪問內存。如下圖所示

循序漸進--理解什麼是Java內存模型

5、單核CPU只含有一套L1,L2,L3緩存;如果CPU含有多個核心,即多核CPU,則每個核心都含有一套L1(甚至和L2)緩存,而共享L3(或者和L2)緩存,下圖是一個單CPU雙核得緩存結構圖:

循序漸進--理解什麼是Java內存模型

隨著計算機能力不斷提升,開始支持多線程,那麼就可能會問題了。我們分別來分析下單線程、多線程在單核CPU、多核CPU中的影響。

  1. 單核cpu與單線程:核心的緩存只被一個線程訪問,緩存獨佔,不會出現訪問衝突等問題
  2. 單核CPU與多線程:進程中的多個線程會同時訪問進程中的共享數據,CPU將某塊內存加載到緩存後,不同線程在訪問相同的物理地址的時候,都會映射到相同的緩存位置,這樣即使發生線程的切換,緩存仍然不會失效。但由於任何時刻只能有一個線程在執行,因此不會出現緩存訪問衝突
  3. 多核CPU與多線程:每個核都至少有一個L1 緩存用於提升效率。當多個線程分別在不同的核心上執行且訪問進程中的同個共享內存,由於多核是可以並行的,則可能會出現類似多線程編程中出現的併發問題,如對於同一塊內存中的變量,多個核心同時讀寫修改數據的話,就會出現不可預期的錯誤,而其解決思路則是通過鎖機制。

所以在CPU和主存之間增加緩存,在多核CPU多線程場景下發生併發內存訪問操作時可能會出現歧義。

處理器優化--“指令重排”

除了上面的問題之外,還有另一個硬件問題也比較重要:處理器為了使其內部的運算單元能夠被充分利用會進行優化,可能會亂序執行處理輸入代碼,此處暫理解為“指令重排”。除了一些處理器會對代碼進行優化亂序處理外,很多編程語言的編譯器也會有類似的優化,比如Java虛擬機的JIT即時編譯器也會做指令重排。

循序漸進--理解什麼是Java內存模型

如Java單例設計模式Double-Check例子中的voliate關鍵字應用就是為了防止因指令重排導致在多線程併發場景下出現異常。感興趣的朋友可以參考我的另一篇文章 ,這裡就不再進行過多的探討。

什麼是內存模型

上面分析了那麼多其實目的是為了引出兩個重要問題

  • 多核CPU多線程場景下發生併發內存訪問操作時可能會出現歧義
  • 處理器為了使其內部的運算單元能夠被充分利用會自行進行優化--“指令重排”

那麼對於以上問題該如何解決?這時候便引出了重要概念--內存模型,定義如下:

內存模型是對內存進行讀寫訪問過程的抽象,可以理解為內存模型定義了共享內存系統中讀寫操作行為的規範,通過這些規則來規範對內存的讀寫操作,從而保證指令執行的正確性。它與處理器有關、與緩存有關、與併發有關、與編譯器也有關。目的是為解決CPU多級緩存、處理器優化、指令重排等導致的問題與歧義,

可以簡單理解為內存模型其實就是解決多線程場景下因併發所導致的問題的一個重要規範

循序漸進--理解什麼是Java內存模型

Java內存模型

1、定義

Java內存模型(Java Memory Model ,JMM)就是一種符合內存模型規範的,屏蔽了各種硬件和操作系統的訪問差異的,以實現讓Java程序在各種平臺下都能達到一致的內存訪問效果的規範。

2、理解

Java內存模型(簡稱“JMM”)是一個規範,其主要目的是定義程序中各種變量的訪問規則,是圍繞著在併發過程中如何處理原子性可見性有序性三個特徵來建立的。

循序漸進--理解什麼是Java內存模型

關於Java內存模型的實現,相信熟悉Java併發編程的朋友一定會熟悉,Java提供了一系列和併發處理相關的關鍵字,其實這些就是Java內存模型封裝了底層的實現後提供給程序員使用的一些關鍵字,本文在這裡就不對這些關鍵字一一展開討論了,感興趣的朋友可以看看《Java多線程編程核心技術》進行了解。其中如

  • 通過使用volatile關鍵字解決因指令重排導致的問題
  • 通過synchronized關鍵字來保證線程安全等

等方式其實就與硬件通過計算機內存模型限制處理器優化使用內存屏障等解決問題的思路一致。

循序漸進--理解什麼是Java內存模型

最後

本文從硬件可能出現的問題引出計算機內存模型解決方案,進而提取出多線程併發問題。最後再引出Java內存模型。Java內存模型可以簡單理解就是一套圍繞著在併發過程中如何處理原子性、可見性和有序性三個特徵來建立的規範,而如synchronized等關鍵字則是其具體實現。

感謝您的閱讀,如果喜歡本文歡迎關注和轉發,本頭條號將堅持原創,持續分享IT技術知識。對於文章內容有其他想法或意見建議等,歡迎提出共同討論共同進步

參考文章及書籍:

《深入理解Java虛擬機:JVM高級特性與最佳實現(第3版)》

《Java多線程編程核心技術》

HollisChuang 鏈接:https://juejin.im/post/5b42c01ee51d45194e0b819a


分享到:


相關文章: