首先給出定義,Java
內存模型(Java Memory Model
,JMM
)是一種符合內存模型規範的,屏蔽了各類硬件和操做系統的訪問差別的,保證了Java
程序在各類平臺下對內存的訪問都能保證效果一致的機制及規範。程序員
在弄懂JMM
以前,咱們要先了解下CPU
和內存是如何交互的。編程
從圖中能夠看出在多
CPU
的系統中,每一個CPU
都有都有各自的高速緩存,通常分爲L1
、L2
、L3
緩存,由於這些緩存的存在,提供了數據的訪問性能,也減輕了數據總線上數據傳輸的壓力,而主內存卻只有一個 。CPU
要讀取一個數據時,首先從一級緩存中查找,若是沒有找到再從二級緩存中查找,若是仍是沒有就從三級緩存或內存中查找,每一個CPU
有且只有一套本身的緩存。緩存
可是問題也就來了,若是兩個CPU
同時去操做同一個內存地址,會發生什麼?也就是說,如何保證多個處理器運算涉及到同一個內存區域時,多線程場景下的緩存一致性問題?運行時如何保證數據一致性?那就是內存屏障(Memory Barrier
)。安全
內存屏障多線程
CPU
中的高速緩存提升了數據訪問性能,避免每次都向內存索取,可是不能實時的和內存發生信息交換。在不一樣CPU
執行的不一樣線程對同一個變量的緩存值多是不一樣的,由此就出現了內存屏障,硬件層的內存屏障分爲兩種:Load Barrier
和 Store Barrier
即讀屏障和寫屏障。架構
內存屏障的做用主要有兩點:併發
之因此扯了那麼多計算機內存模型,是由於Java
內存模型的設定符合了計算機的規範。函數
實際上,JMM
是JVM
的一種規範,定義了JVM
的內存模型。它屏蔽了各類硬件和操做系統的訪問差別,不像C
那樣直接訪問硬件內存,相對安全不少。它的主要目的是解決因爲多線程經過共享內存進行通訊時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。能夠保證併發編程場景中的原子性、可見性和有序性。性能
Java
中的幾個關鍵字:volatile
、final
、synchronized
,能夠幫助程序員把代碼中的併發需求描述給編譯器。Java
內存模型中定義了它們的行爲,以確保正確同步的Java
代碼在全部的處理器架構上都能正確執行。操作系統
volatile
在Java
中,volatile
關鍵字能夠解決上面的問題,Java
屏蔽掉這些差別,經過JVM
生成內存屏障的指令。
當咱們聲明某個變量爲volatile
修飾時,這個變量就有了線程可見性,volatile
會在讀寫操做先後添加內存屏障。volatile
字段的每次讀行爲都能看到其它線程最後一次對該字段的寫行爲,經過它就能夠避免拿到緩存中陳舊數據。它們必須保證在被寫入以後,會被刷新到主內存中,這樣就能夠當即對其它線程能夠見。
final
若是一個類包含final
字段,且在構造函數中初始化,那麼正確的構造一個對象後,final
字段被設置後對於其它線程是可見的。
注意這裏所說的正確構造對象,意思是在對象的構造過程當中,不容許對該對象進行引用,否則的話,可能存在其它線程在對象還沒構造完成時就對該對象進行訪問,形成其餘的問題。
synchronized
對於一個被synchronized
修飾的monitor
對象,只可以被一個線程持有,意味着一旦有線程進入了同步代碼塊,那麼其它線程就不能進入,直到第一個進入的線程退出代碼塊。
在一個線程退出同步塊時,線程釋放monitor
對象,它的做用是把CPU
緩存數據(本地緩存數據)刷新到主內存中,從而實現該線程的行爲能夠被其它線程看到。在其它線程進入到該代碼塊時,須要得到monitor
對象,它在做用是使CPU
緩存失效,從而使變量從主內存中從新加載,而後就能夠看到以前線程對該變量的修改。
但從緩存的角度看,這個問題只會影響多處理器的機器,對於單核來講沒什麼問題,可是它還有一個語義是禁止指令的重排序,對於編譯器來講,同步塊中的代碼不會移動到獲取和釋放monitor
的外面。
JMM
是JVM
的一種規範,定義了JVM
的內存模型。它的主要目的是解決因爲多線程經過共享內存進行通訊時,存在的本地內存數據不一致、編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。能夠保證併發編程場景中的原子性、可見性和有序性。
在Java
中,volatile
、final
、synchronized
這三個關鍵字是對與內存模型的具體實現。
本文由博客一文多發平臺 OpenWrite 發佈!
更多內容請點擊個人博客 沐晨