Java內存模型(JMM)
JMM用來屏蔽不一樣硬件和操做系統的內存訪問差別,指望Java程序在各類平臺上都能實現一致的內存訪問效果;
JMM規定裏多線程之間的共享變量存儲在主存中,
每一個線程單獨擁有一個本地內存(
邏輯概念
),本地內存存儲線程操做的共享變量副本;
- JMM中的變量指的是線程共享變量(實例變量,static字段和數組元素),不包括線程私有變量(局部變量和方法參數);
- JMM規定線程對變量的寫操做都在本身的本地內存對副本進行,不能直接寫主存中的對應變量;
- 多線程間變量傳遞經過主存完成(Java線程通訊經過共享內存),線程修改變量後經過本地內存寫回主存,從主存讀取變量,彼此不容許直接通訊(本地內存私有緣由);
綜上,JMM經過控制主存和每一個線程的本地內存的數據交互,保證
一致的內存
可見性;
JMM特徵
原子性:原子性是指多線程一塊兒執行時,一個線程操做開始後不會被其餘線程干擾,操做不可被中斷;
- synchronizd臨界區執行具備原子性;
- volatile僅僅保證對單個volatile變量的操做具備原子性;
可見性:一個線程修改共享變量時,其餘線程可以當即知道這個修改;
- 單線程:不存在內存可見性問題;
- 多線程:Java經過volatile, synchronized, final關鍵字實現可見性;
- volatile:valatile變量保證變量新值當即被同步回主存,每次讀取valtile變量都當即從主存刷新;
- synchronized:對變量進行解鎖前,將對應變量同步回內存;
- final:final字段一旦初始化完畢,而且this引用沒有發生逃逸,其餘線程當即看到final字段值;
有序性:線程內操做有序進行,線程間操做有序進行;
- Java經過volatile, synchronized保證線程間操做的有序性
- volatile經過禁止重排序實現有序性;
- synchronized經過聲明臨界區,保證線程互斥訪問,實現有序性;
synchronized與volatile辨析
- volatile是線程同步的輕量級實現,只用於修飾變量,synchronized用於修飾方法和語句塊;
- 多線程訪問volatile不會發生阻塞,可是synchronized會發生阻塞;
- volatile保證數據的可見性,不保證原子性;synchronized保證數據的可見性和原子性;
- volatile強調共享變量在多線程之間的可見性,synchronized強調多線程訪問資源的同步性;
重排序與happen-before規則
影響多線程有序性:重排序
- 編譯器重排序:編譯器保證不改變單線程執行結果的前提下,能夠調整多線程語句執行順序;
- 處理器重排序:若是不存在數據依賴性,處理器能夠改變語句對應機器指令的執行順序;
JMM經過happen-before規則,底層禁止特定類型的編譯器重排序和處理器重排序,保證內存的可見性和有序性
;
happen-before規則
JMM爲全部操做定義了一個偏序關係,稱之爲happen-before。
在JMM中,若是A操做對B操做存在happen-before關係;A操做的執行結果所有對B操做可見;
所以不一樣操做的時間順序和先行發生規則沒有關係,happen-before強調前者修改結果所有對後者可見;若是A,B操做不存在happen-before關係,JVM會對它們進行任意重排序;
JMM默認
happen-before
規則:
- 程序順序規則:一個線程的每一個操做,先於該線程其餘後續操做執行;
- 線程的start()方法先於線程內其餘方法執行,線程全部操做先於線程的終結操做;
- 鎖規則:對一個monitor的解鎖必然先於對該monitor的加鎖;
- volatile變量規則:對volatile的寫操做先於讀操做;
- 傳遞性:A先於B,B先於C,必然A先於C;
內存屏障指令(memory barriers)
內存屏障指令是一組處理指令,用來限制內存操做的順序;
volatile關鍵字
★ volatile實現原理
volatile變量寫,彙編指令會多出
Lock前綴,Lock前綴在多核處理器下的做用:
- 將當前處理器緩存行的數據寫回主存;
- 令其餘CPU裏緩存該內存地址的數據無效;
針對編譯器重排序:
JMM針對編譯器指定了volatile重排序規則表,規定哪些前後操做不能進行編譯器重排序:
針對處理器重排序:編譯器在生成字節碼指令時,經過在指令序列中插入內存屏障指令來禁止特定類型的處理器重排序,以
實現volatile內存語義
:
volatile底層經過內存屏障指令實現
- 在每一個volatile變量寫操做以前插入StoreStore屏障,以後插入StoreLoad屏障;
- 以前插入StoreStore屏障:禁止volatile寫以前的寫操做與其重排序,保證以前的全部寫操做都寫回主存,對volatile寫可見;
- 以後插入StoreLoad屏障:禁止volatile寫以後的讀寫操做與其重排序,實現volatile寫結果對後續操做可見;
- 在每一個volatile變量讀操做以後,接連插入LoadLoad屏障,LoadStore屏障;
- 插入LoadLoad屏障:禁止volatile變量讀以後的讀操做與其重排序;
- 插入LoadStore屏障:禁止volatile變量讀以後的寫操做與其重排序;
- 經過插入兩次內存屏障,實現volatile讀結果對後續操做可見;
JMM經過上述內存屏障插入策略,保證在任意平臺上
volatile
的內存語義一致;
volatile關鍵字語義
volatile用來修飾共享變量(成員變量,static變量)代表:
- volatile變量寫:當寫一個volatile變量時,JMM會把全部線程本地內存的對應變量副本刷新回主存;
- volatile變量讀:當讀一個volatile變量時,JMM會設置該線程的volatile變量副本(本地內存中)無效,線程只能從主存中讀取該變量;
- 保證了volatile變量讀,總能看見對該volatile變量最後的修改;
- volatile變量讀和加鎖內存語義相同;
經過上述機制,
volatile
保證共享變量一旦被修改,新值對全部線程可見;