不知道爲何,寫文章的時候就想在最上面放一張圖片,多是由於強迫症吧,最近買了一些java併發相關書籍在看,若是沒有大的問題,最近幾篇文章應該都是記錄java內存模型相關內容了(以下圖),內容有點多,因此準備分開來寫,寫的緣由主要想記錄一下,還有就是以爲本身太菜有些看不懂的,想寫出來看有沒有大佬解答如下的,如題,今天要寫的就是java內存模型的基礎:java
通訊是指線程之間以何種機制來交換信息。在命令式編程中,線程之間通訊機制有兩種:共享內存和消息傳遞。程序員
共享內存:線程之間共享程序的公共狀態,經過寫-讀內存中的公共狀態進行隱式通訊。編程
消息傳遞:線程之間沒有公共狀態,線程之間必須經過發送消息來顯式的進行通訊。數組
Java的併發採用的是共享內存模型。緩存
同步是指程序中用於控制不一樣線程間操做發生相對順序的機制。多線程
共享內存:在共享內存模型中,同步是顯式進行的,程序員必須顯式指定某個方法或某段代碼須要在線程之間互斥執行。併發
消息傳遞:在消息傳遞模型中,因爲消息的發送必須在消息接收以前,所以同步是隱式進行的。app
Java的併發採用的是共享內存模型,Java線程之間的通訊老是隱式進行,整個通訊過程對程序員徹底透明。性能
在Java中,全部實例域、靜態域和數組元素都存儲在堆內存中,堆內存在線程之間共享,(本文用「共享變量」這個術語來代指實例域、靜態域和數組元素),局部變量、方法定義參數和異常處理器參數不會在線程之間共享,他們不會有內存可見性的問題,也不會受到內存模型的影響。學習
Java線程之間的通訊由Java內存模型(JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關係:線程之間的共享變量存儲在主內存中,每一個線程都有一個私有的本地內存,本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個概念,並不真實存在。它涵蓋了緩存、寫緩衝區、寄存器以及其餘的硬件和編譯器優化。Java內存模型的抽象示意圖以下所示:
如圖所示,線程A與線程B要通訊的話,必須通過下面兩個步驟:一、線程A把本地內存中更新過的共享變量刷新到主內存中。
二、線程B去主內存讀取線程A更新過的共享變量。
經過下圖來講明這兩個步驟:
如圖,本地內存A和本地內存B有主內存中共享變量x的副本。假設初識時,這三個內存中的x值都是0。線程A在執行時,把更新後的x值(假設值爲1)臨時存放在本身的本地內存A中,當線程A和線程B須要通訊時,線程A首先會把本身本地內存中修改後的x值刷新到主內存中,此時主內存的x值變爲了1,隨後,線程B去主內存讀取線程A更新後的x值,此時線程B的本地內存的x值也變爲了1。
從總體來看,這兩個步驟的實質上是線程A再向線程B發送消息,並且這個通訊過程必需要通過主內存。JMM經過控制主內存與每一個線程的本地內存之間的交互,來爲Java程序員提供內存的可見性保證。
在執行程序時,爲了提升性能,編譯器和處理器經常會對指令作重排序。重排序分爲3種類型:
1)編譯器優化的重排序:編譯器在不改變單線程程序語義的前提下,能夠從新安排語句的執行順序。
2)指令級並行的重排序:現代處理器採用了指令級並行計數來將多條指令重疊執行。若是不存在數據依賴性,處理器能夠改變語句對應機器指令的執行順序。
3)內存系統的重排序:因爲處理器使用緩存和讀/寫緩衝區,這使得加載和存儲操做看上去多是在亂序執行。
從Java源代碼到最終實際執行的指令序列,會分別經歷下面3種排序,以下圖所示:
其中1屬於編譯器重排序,2和3屬於處理器重排序。這些重排序可能會致使多線程程序出現內存可見性問題。對於編譯器,JMM的編譯器重排序規則會禁止特定類型的編譯器重排序(不是全部的編譯器重排序都要禁止)。對於處理器的重排序,JMM的處理器重排序規則會要求Java編譯器在生成指令序列時,插入特定類型的內存屏障指令,經過內存屏障指令來禁止特定類型的處理器重排序。
JMM屬於語言級的內存模型,它確保在不一樣的編譯器和不一樣的處理器平臺之上,經過禁止特定類型的編譯器重排序和處理器重排序,爲程序員提供一致的內存可見性保證。
從JDK5開始,Java使用新的JSR-133內存模型,JSR-133內存模型使用happens-before的概念來闡述操做之間的內存可見性。在JMM中,若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必需要存在happens-before關係。這裏提到的兩個操做既能夠是在一個線程以內,也能夠是在不一樣線程之間。
與程序員密切相關的happens-before規則以下:
1)程序順序規則:一個線程中的每一個操做,happens-before於該線程中的任意後續操做。
2)監視器鎖規則:對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。
3)volatile變量規則:對一個volatile域的寫,happens-before於任意後續對這個volatile域的讀。
4)傳遞性:若是A happens-before B,而且B happens-before C,那麼A happens-before C。
兩個操做之間具備happens-before關係,並不意味着前一個操做必需要在後一個操做以前執行!happens-before僅僅要求前一個操做(執行的結果)對後一個操做可見,且前一個操做按順序排在第二個操做以前。
happens-before與JMM的關係以下圖所示:
由圖可知,一個happens-before規則對應於一個或多個編譯器和處理器重排序規則。對程序員來講,happens-before規則簡單易懂,它避免程序員爲了理解JMM提供的內存可見性保證而去學習複雜的重排序規則以及這些規則的具體實現方法。