JMM (Java Memory Model) java內存模型java
Java線程之間的通訊由Java內存模型(本文簡稱爲JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。緩存
Java內存模型規定了全部的變量都存儲在主內存(Main Memory)中。每條線程還有本身的工做內存(Working Memory,可與前面講的處理器高速緩存類比),線程的工做內存中保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的全部操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成,線程、主內存、工做內存三者的交互關係如圖所示。多線程
本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存,寫緩衝區,寄存器以及其餘的硬件和編譯器優化app
爲了程序可以更高效的運行,編譯器和處理器都會對指令進行重排序;重排序分爲如下三種類型:優化
只要是重排序都有可能會致使多線程內出現內存可見性的問題spa
爲了保證內存可見性,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會使該屏障以前的全部內存訪問指令(存儲和裝載指令)完成以後,才執行該屏障以後的內存訪問指令。 |
若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必須存在happens-before關係。這裏提到的兩個操做既能夠是在一個線程以內,也能夠是在不一樣線程之間。blog
happens-before規則以下:排序
注意,兩個操做之間具備happens-before關係,並不意味着前一個操做必需要在後一個操做以前執行!happens-before僅僅要求前一個操做(執行的結果)對後一個操做可見,且前一個操做按順序排在第二個操做以前內存
JMM對正確同步的多線程程序,其執行將具備順序一致性(sequentially consistent)---即程序的執行結果和在順序一致性模型中獲得的結果相同;(這裏同步是指廣義上的同步,包括同步原語(lock,volatile,final)的正確使用)
特性: