內存模型 (memory model) java
內存模型描述的是程序中各變量(實例域、靜態域和數組元素)之間的關係,以及在實際計算機系統中將變量存儲到內存和從內存取出變量這樣的低層細節. 數組
不一樣平臺間的處理器架構將直接影響內存模型的結構. 緩存
在C或C++中, 能夠利用不一樣操做平臺下的內存模型來編寫併發程序. 可是, 這帶給開發人員的是, 更高的學習成本.相比之下, Java利用了自身虛擬機的優點, 使內存模型不束縛於具體的處理器架構, 經過Java內存模型真正實現了跨平臺.(針對hotspot jvm, jrockit等不一樣的jvm, 內存模型也會不相同) 多線程
內存模型的特徵: 架構
a, Visibility 可視性 (多核,多線程間數據的共享) 併發
b, Ordering 有序性 (對內存進行的操做應該是有序的) app
Java內存模型 ( java memory model ) jvm
根據Java Language Specification中的說明, jvm系統中存在一個主內存(Main Memory或Java Heap Memory),Java中全部變量都儲存在主存中,對於全部線程都是共享的。 工具
每條線程都有本身的工做內存(Working Memory),工做內存中保存的是主存中某些變量的拷貝,線程對全部變量的操做都是在工做內存中進行,線程之間沒法相互直接訪問,變量傳遞均須要經過主存完成。 學習
其中, 工做內存裏的變量, 在多核處理器下, 將大部分儲存於處理器高速緩存中, 高速緩存在不通過內存時, 也是不可見的.
jmm怎麼體現可視性(Visibility) ?
在jmm中, 經過併發線程修改變量值, 必須將線程變量同步回主存後, 其餘線程才能訪問到.
jmm怎麼體現有序性(Ordering) ?
經過Java提供的同步機制或volatile關鍵字, 來保證內存的訪問順序.
緩存一致性(cache coherency)
什麼是緩存一致性?
它是一種管理多處理器系統的高速緩存區結構,其能夠保證數據在高速緩存區到內存的傳輸中不會丟失或重複。(來自wikipedia)
舉例理解:
假若有一個處理器有一個更新了的變量值位於其緩存中,但尚未被寫入主內存,這樣別的處理器就可能會看不到這個更新的值.
解決緩存一致性的方法?
a, 順序一致性模型:
要求某處理器對所改變的變量值當即進行傳播, 並確保該值被全部處理器接受後, 才能繼續執行其餘指令.
b, 釋放一致性模型: (相似jmm cache coherency)
容許處理器將改變的變量值延遲到釋放鎖時才進行傳播.
Java內存模型的緩存一致性模型 - "happens-before ordering(先行發生排序)"
通常狀況下的示例程序:
x = 0; y = 0; i = 0; j = 0; // thread A y = 1; x = 1; // thread B i = x; j = y;
在如上程序中, 若是線程A,B在無保障狀況下運行, 那麼i,j各會是什麼值呢?
答案是, 不肯定. (00,01,10,11都有可能出現),這裏沒有使用Java同步機制, 因此Java內存模型有序性和可視性都沒法獲得保障. happens-before ordering( 先行發生排序) 如何避免這種狀況? 排序原則已經作到:
a, 在程序順序中, 線程中的每個操做, 發生在當前操做後面將要出現的每個操做以前.
b, 對象監視器的解鎖發生在等待獲取對象鎖的線程以前.
c, 對volitile關鍵字修飾的變量寫入操做, 發生在對該變量的讀取以前.
d, 對一個線程的 Thread.start() 調用 發生在啓動的線程中的全部操做以前.
e, 線程中的全部操做 發生在從這個線程的 Thread.join()成功返回的全部其餘線程以前.
爲了實現 happends-before ordering原則, Java及JDK提供的工具:
a, synchronized關鍵字
b, volatile關鍵字
c, final變量
d, java.util.concurrent.locks包(since jdk 1.5)
e, java.util.concurrent.atmoic包(since jdk 1.5)
使用了happens-before ordering的例子:
1) 獲取對象監視器的鎖(lock)
(2) 清空工做內存數據, 從主存複製變量到當前工做內存, 即同步數據 (read and load)
(3) 執行代碼,改變共享變量值 (use and assign)
(4) 將工做內存數據刷回主存 (store and write)
(5) 釋放對象監視器的鎖 (unlock)
注意: 其中4,5兩步是同時進行的.
這邊最核心的就是第二步, 他同步了主內存,即前一個線程對變量改動的結果,能夠被當前線程獲知!(利用了happens-before ordering原則)
對比以前的例子
若是多個線程同時執行一段未經鎖保護的代碼段,頗有可能某條線程已經改動了變量的值,可是其餘線程卻沒法看到這個改動,依然在舊的變量值上進行運算,最終致使不可預料的運算結果。