精進之路之JMM

JMM (Java Memory Model) java內存模型java

Java內存模型的抽象

Java線程之間的通訊由Java內存模型(本文簡稱爲JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。緩存

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

本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存,寫緩衝區,寄存器以及其餘的硬件和編譯器優化app

 

1. 重排序

爲了程序可以更高效的運行,編譯器和處理器都會對指令進行重排序;重排序分爲如下三種類型:優化

  1. 編譯器優化的重排序
  2. 指令級並行的重排序
  3. 內存系統的重排序

只要是重排序都有可能會致使多線程內出現內存可見性的問題spa

1.1 內存屏障指令

爲了保證內存可見性,java編譯器在生成指令序列的適當位置會插入內存屏障指令來禁止特定類型的處理器重排序。JMM把內存屏障指令分爲下列四類:線程

屏障類型 指令示例 說明
LoadLoad Barriers Load1; LoadLoad; Load2 確保Load1數據的裝載,以前於Load2及全部後續裝載指令的裝載。
StoreStore Barriers Store1; StoreStore; Store2 確保Store1數據對其餘處理器可見(刷新到內存),以前於Store2及全部後續存儲指令的存儲。
LoadStore Barriers Load1; LoadStore; Store2 確保Load1數據裝載,以前於Store2及全部後續的存儲指令刷新到內存。
StoreLoad Barriers Store1; StoreLoad; Load2 確保Store1數據對其餘處理器變得可見(指刷新到內存),以前於Load2及全部後續裝載指令的裝載。StoreLoad Barriers會使該屏障以前的全部內存訪問指令(存儲和裝載指令)完成以後,才執行該屏障以後的內存訪問指令。

1.2 happens-before

若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必須存在happens-before關係。這裏提到的兩個操做既能夠是在一個線程以內,也能夠是在不一樣線程之間。blog

happens-before規則以下:排序

  • 程序順序規則:一個線程中的每一個操做,happens- before 於該線程中的任意後續操做。
  • 監視器鎖規則:對一個監視器鎖的解鎖,happens- before 於隨後對這個監視器鎖的加鎖。
  • volatile變量規則:對一個volatile域的寫,happens- before 於任意後續對這個volatile域的讀。
  • 傳遞性:若是A happens- before B,且B happens- before C,那麼A happens- before C。

注意,兩個操做之間具備happens-before關係,並不意味着前一個操做必需要在後一個操做以前執行!happens-before僅僅要求前一個操做(執行的結果)對後一個操做可見,且前一個操做按順序排在第二個操做以前內存

2. 順序一致性模型

JMM對正確同步的多線程程序,其執行將具備順序一致性(sequentially consistent)---即程序的執行結果和在順序一致性模型中獲得的結果相同;(這裏同步是指廣義上的同步,包括同步原語(lock,volatile,final)的正確使用)

特性:

  • 一個線程中全部操做都必須按照程序的順序來執行
  • 無論程序是否同步,全部線程都只能看到一個單一的操做執行順序。在此模型下,每一個操做都必須原子執行且當即對全部線程可見
本文轉自: https://www.jianshu.com/p/75f512f22c23,感謝原做者的精彩分享
相關文章
相關標籤/搜索