》》》》》》博客地址《《《《《《
》》》》》》首發博客《《《《《《java
首先咱們在瞭解java內存模型以前先看一下計算機內存模型,理解了計算機內存模型的話後面在看JMM就會簡單的多,上篇文章我是直接寫的。緩存
計算機是由CPU、主存、磁盤等組成的(簡單引出問題熬)咱們都知道計算機執行程序的指令都是由CPU來執行的,執行的時候是要處理數據的,這些數據一般存儲在主存中。多線程
如圖所示,這時候問題來了,CPU的執行速度愈來愈快,而後內存卻是沒什麼進展,這樣的話CPU的讀寫操做就會很是耗時,效率不就很低了?併發
因此這個時候就出現了高速緩存(Cache)來解決這個問題,那麼緩存是什麼呢?緩存其實就是保存的數據備存,特色是快。因此這個時候程序的執行過程就變成了這個樣子:首先在運行的時候會把數據從主存中賦值一份放在緩存中,而後CPU在運算的時候就直接去緩存中讀寫數據,等執行結束後在把數據刷新到主存中。這樣一來就大大的提升了執行的速度。咱們來看一下流程圖:優化
能夠看出,運行的時候L1緩存先把數據從主存中讀取出來,而後CPU操做的數據是從緩存中讀取,當數據執行完畢,在從緩存中刷新到主存中。隨着CPU的執行能力愈來愈強,一層緩存已經知足不了需求了,這時候就出現了2級緩存(L2Cache)3級緩存(L3Cache),每級緩存都存儲的是下一級緩存的一部分數據。操作系統
那麼當CPU須要數據的時候就會這樣執行:首先去一級緩存(L1Cache)查找,若是一級緩存沒有就去二級緩存(L2Cache)查找,二級緩存沒有就去三級緩存(L3Cache)查找,若是緩存中沒有,就去主存中查找。 那麼問題來了。線程
現代計算機已經不是單個CPU,有多個CPU每一個CPU還可能會有多核,單核CPU只有一套緩存分別就是上面所說的L一、L二、L3如圖所示:blog
若是CPU有多個核心的話,就是每一個核心都有L1緩存或者有L2緩存,而共享L3緩存或者L2緩存。排序
咱們來看一下結構圖:內存
這個時候每一個核心都有本身的高速緩存,它們又共享同一主存,就會形成緩存一致性的問題,在多線程同時訪問同一共享數據的狀況下,每一個線程都是操做本身緩存的數據副本,這個時候就會出現每一個緩存中的共享數據存在不一致的狀況。多個處理器運算任務都涉及同一塊主存,須要一種協議能夠保障數據的一致性,這類協議有MSI、MESI、MOSI及Dragon Protocol等。
上面瞭解到提升CPU的效率就是在CPU和主存直接增長高速緩存,增長高速緩存會形成緩存不一致的問題,除了緩存不一致的問題,還有一種問題就是爲了能讓處理器內部的運算單元可以儘可能的被充分利用處理器可能會對輸入代碼進行亂序執行,而且處理器會在計算以後將亂序的代碼進行結果重組來保證結果的一致性。在Java虛擬機中也有相似的指令重排序。
這篇文章實際上是講述java內存模型的,爲何會和計算機硬件扯上關係呢?注意到上面有說到多線程的狀況下會形成緩存不一致的問題,提到多線程就離不開併發,想到併發的話就離不開三大問題,可見性,原子性,有序性的問題。那這三種特性不就是上面所說到的緩存不一致,處理器優化和指令重排序問題嗎。這這樣看來緩存不一致不就是可見性的問題,而原子性不就是處理器優化所致使的原子性問題,指令重排序就是致使有序性的問題。那麼Java內存模型又是什麼呢?
Java內存模型的做用就是用來屏蔽掉不一樣操做系統中的內存差別性來保持併發的一致性。同時JMM也規範了JVM如何與計算機內存進行交互。簡單的來講java內存模型就是Java本身的一套協議來屏蔽掉各類硬件和操做系統的內存訪問差別,實現平臺一致性達到最終的"一次編寫,處處運行"。看到這裏就知道了Jmm是用來作什麼的。同時Java內存模型能夠理解爲java併發內存模型。而後JMM
Java內存模型(如下簡稱JMM)規定了,全部變量都存儲在主內存中,每一個線程都有本身的本地緩存,因此線程中對變量的操做都必須在本地緩存中進行並非直接操做主內存,線程之間的沒法訪問對方線程的變量,想要通訊的話就只能經過主內存進行通訊。
JMM抽象示意圖:
從上圖能夠看出每一個線程都有一個本地內存,若是線程想要通訊的話要執行一下步驟:
具體通訊規則能夠參考我上一篇文章:Java內存模型裏面定義了八種通訊規則。
到這裏就對JMM有個清晰的理解了。JMM實際上是一種規範,其主要目的就是爲了解決多線程經過共享內存進行通訊時所產生的本地內存數據不一致,編譯器會對代碼指令重排序、處理器會對代碼亂序執行等帶來的問題。
JMM所解決的問題離不開咱們上面所說的三大特性:可見性、原子性、有序性.
原子性:在java中使用synchronized關鍵字保證代碼的原子性,synchronized實現原理後面會單獨寫一篇文章。
可見性:volatile關鍵字保證了多線程操控變量的可見性,同時synchronized和final也能夠保證變量的可見性,注意:volatile並不保證原子性,因此何時用volatile必定要注意。
有序性:volatile能夠禁用指令重排,synchronized關鍵字保證同一時刻只容許一條線程操做因此咱們能夠發現synchronized能夠解決三種問題,因此使用synchronized關鍵字比較多,可是synchronized只容許一個線程進行操做,會形成上下文切換的效率問題。
經過上文必定對JMM是什麼,和有什麼做用有了必定的理解這裏推薦《深刻理解Java虛擬機》