併發編程這一塊內容,是高級資深工程師必備知識點,25K起若是不懂併發編程,那基本到頂。可是併發編程內容龐雜,如何系統學習?本專題將會系統講解併發編程的全部知識點,包括但不限於:java
線程通訊機制,深刻JMM內存模型原理,深刻synchronized原理,深刻volatile原理,DCL,詳解AQS,CAS,可重入鎖,讀寫鎖原理,詳解併發工具類,深刻理解threadLocal,Fork、Join,原子類詳解,Java併發集合詳解(ConcurrentHashMap,ConcurrentLinedQueue,ConcurrentListMap等),阻塞隊列深刻探究,深刻線程池原理及其設計思想。 本文爲深刻理解java內存模型。程序員
主線如上圖紅色箭頭,你們能夠先看看總體講的是什麼。java內存模型前面是鋪墊,後面是相關內容。面試
共享變量(實例域,靜態域,數組元素)纔會用到。 局部變量,方法定義參數等不會在線程間共享,因此他們不會有內存可見性問題,也不受內存模型影響編程
Java內存模型簡稱JMM(Java Memory Model),是Java虛擬機所定義的一種抽象規範,用來屏蔽不一樣硬件和操做系統的內存訪問差別,讓java程序在各類平臺下都能達到一致的內存訪問效果。數組
主內存能夠簡單理解爲計算機當中的內存,但又不徹底等同。主內存被全部的線程所共享,對於一個共享變量(好比靜態變量,或是堆內存中的實例)來講,主內存當中存儲了它的「本尊」。緩存
本地內存能夠簡單理解爲計算機當中的CPU高速緩存,但又不徹底等同。每個線程擁有本身的工做內存,對於一個共享變量來講,工做內存當中存儲了它的「副本」。爲啥有本地內存這個概念?由於直接操做主內存太慢了安全
經過一系列內存讀寫的操做指令(JVM內存模型共定義了8種內存操做指令,之後會細講),線程A把靜態變量 s=0 從主內存讀到工做內存,再把 s=3 的更新結果同步到主內存當中。從單線程的角度來看,這個過程沒有任何問題。多線程
理解重排序前這個概念前,咱們先轉換場景,從java內存模型走出來,來到硬件CPU這個維度。併發
在執行程序時爲了提升性能,編譯器和處理器經常會對指令作重排序(簡單理解就是本來咱們寫的代碼指令執行順序應該是A→B→C,可是如今的CPU都是多核CPU,爲了秀下優越,爲了提升並行度,爲了提升性能等,可能會出現指令順序變爲B→A→C等其餘狀況)。app
固然CPU們也不是隨便就去重排序,須要知足如下兩個條件(遵循的規則):
從 Java 源代碼到最終實際執行的指令序列,會分別經歷下面三種重排序:
那麼重排序會遵循什麼樣的規則?
無論怎麼重排序,(單線程)程序的執行結果不能被改變。編譯器,runtime和處理器都必須遵照as-if-serial語義。OK,這就至關於給CPU們定下規則。不要隨便重排序。要知足我這個as-if-serial的前置條件,才能重排序。
as-if-serial語義把單線程程序保護了起來,遵照as-if-serial語義的編譯器,runtime和處理器共同爲編寫單線程程序的程序員建立了一個幻覺:單線程程序是按程序的順序來執行的。as-if-serial語義使程序員沒必要擔憂單線程中重排序的問題干擾他們,也無需擔憂內存可見性問題。
注意:as-if-serial只保證單線程環境,多線程環境下無效。那多線程,併發編程下怎麼辦?
上面的這些重排序均可能致使多線程程序出現內存可見性問題,JMM那麼如何解決?
JMM屬於語言級的內存模型,它確保在不一樣的編譯器和不一樣的處理器平臺之上,經過禁止特定類型的編譯器重排序和處理器重排序,爲程序員提供一致的內存可見性保證。
內存屏障也稱爲內存柵欄或柵欄指令,是一種屏障指令,它使CPU或編譯器對屏障指令以前和以後發出的內存操做執行一個排序約束。
volatile即是基於內存屏障實現的。 **觀察加入volatile關鍵字和沒有加入volatile關鍵字時所生成的彙編代碼發現,加入volatile關鍵字時,會多出一個lock前綴指令。**這個指令就至關於一個內存屏障。具體表現爲:
從而保證了,若是某個線程對volatile修飾的共享變量進行更新,那麼其餘線程能夠立馬看到這個更新,這就是所謂的線程可見性。
(注,關於volatile會在後面單章講解,這裏不過於贅婿)
從jdk5開始,java使用新的JSR-133內存模型,基於happens-before的概念來闡述操做之間的內存可見性。 換句話說,在JMM中,若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必須存在happens-before關係。 happens-before原則是JMM中很是重要的原則,它是判斷數據是否存在競爭、線程是否安全的主要依據,保證了多線程環境下的可見性。 這個的兩個操做既能夠在同一個線程,也能夠在不一樣的兩個線程中。
摘錄一些happens-before規則以下: 一、程序順序規則:一個線程中的每一個操做,happens-before於該線程中任意的後續操做。 二、監視器鎖規則:對一個鎖的解鎖操做,happens-before於隨後對這個鎖的加鎖操做。 三、volatile域規則:對一個volatile域的寫操做,happens-before於任意線程後續對這個volatile域的讀。 四、傳遞性規則:若是 A happens-before B,且 B happens-before C,那麼A happens-before C。 注意:兩個操做之間具備happens-before關係,並不意味前一個操做必需要在後一個操做以前執行!僅僅要求前一個操做的執行結果,對於後一個操做是可見的,且前一個操做按順序排在後一個操做以前。
那麼說了那麼多規則,來看看happens-before與JMM的關係