解釋
JMM(java內存模型JAVA Memory Model,簡稱JMM)本身是一種抽象的概念
并不真實存在
,他描述的是一組規則或規范,通過這組規范,定義了程序中各個變量(包括實例字段,靜態字段和構成數組對象的元素)的訪問方式。
JMM關于同步的規定:
- 線程解鎖前,必須把共享變量的值刷新回主內存
- 線程加鎖前,必須讀取主內存的最新值到自己的工作內存
- 加鎖解鎖是同一把鎖
由于JVM運行程序的實體是線程,而每個線程創建時JVM都會為其創建一個工作內存(有些地方稱為棧空間),工作內存是每個線程的私有數據區域,而Java內存模型中規定所有變量都存儲在 主內存
,主內存是共享內存區域,所有線程都可以訪問,但線程對變量的操作(讀取賦值等)必須在工作內存中進行,首先要將變量從主內存拷貝的自己的工作內存空間,然后對變量進行操作,操作完成后再將變量寫回主內存
,不能直接操作主內存中的變量,各個線程中的工作內存中存儲著主內存中的 變量副本拷貝
,因此不同的線程間無法訪問對方的工作內存,線程間的通信(傳值)必須通過主內存來完成,其簡要訪問過程如下圖:
JMM特性
- 可見性
- 原子性
- 有序性
特性解釋
-
可見性:一個線程操作某個對象,修改了值之后,其余的線程都知道,這就叫可見性
各個線程對主內存中共享變量的操作都是各個線程各自拷貝到自己的工作內存進行操作后再寫回到主內存中的。
這就可能存在一個線程AAA修改了共享變量X的值,但還未寫回主內存時,另外一個線程BBB又對主內存中同一個變量X進行操作,但此時A線程工作內存中共享變量X對線程B來說并不可見
這種工作內存與主內存同步延遲現象就造成了可見性問題
-
原子性:
一個線程在操作某個對象時,對象的值發生更改,并更新回主內存,其余線程還未獲得更新的消息,將值同樣更新回主內存,造成數據丟失的情況,即不保證原子性
-
禁止指令重排
計算機執行程序時,為了提高性能,編譯器和處理器常常會對
指令做重排
源代碼 ==》編譯器優化的重排 ==》指令并行的重排 ==》 內存系統的重排 ==》最終執行的指令
單線程環境里面確保程序最終執行和代碼順序執行的結果一致
處理器在進行重排序時,必須要考慮指令之間的
數據依賴性
多線程環境中線程交替執行,由于編譯器優化重排的存在,兩個線程中使用的變量能否保證一致性是無法確定的,結果無法預測
了解一個概念,內存屏障(Memory Barrier)又稱內存柵欄,是一個CPU指令,他的作用有兩個:
- 一是保證特定操作的執行順序
- 而是保證某些變量的內存可見性(利用該特性,實現volatile的內存可見性)
由于編譯器和處理器都能執行指令重排優化,如果在指令間插入一條Memory Barrier,則會告訴編譯器和CPU,不管什么指令都不能和這條Memory Barrier 指令重排序,也就是說
通過插入內存屏障,禁止在內存屏障前后的指令執行重排序優化
。內存屏障另外一個作用是強制刷出各種CPU的緩存數據,因此任何CPU上的線程都能讀取到這些數據的最新版本。