併發——在操做系統中,是指一個時間段中有幾個程序都處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任一個時刻點上只有一個程序在處理機上運行——源自百度百科html
在併發編程中,咱們須要處理兩個關鍵問題:線程之間如何通訊和線程之間如何同步,後續篇章將圍繞這兩個問題進行介紹。java
本文主要介紹java的通訊機制,剛介紹常見通訊機制主要包括如下兩種方式:程序員
Java的併發採用的是共享內存模型,Java線程之間的通訊老是隱式進行,整個通訊過程對程序員徹底透明。在java中,全部實例域、靜態域和數組元素存儲在堆內存中,堆內存在線程之間共享。編程
Java線程之間的通訊由Java內存模型(本文簡稱爲JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。數組
JMM(Java Memory Model)是JVM規範中定義的一種Java內存模型,它的目的是屏蔽掉各類硬件和操做系統的內存訪問差別,以實現讓Java程序在各類平臺上到能達到一致的內存訪問效果。 Java內存模型的主要定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存中取出變量這樣底層細節。首先簡單說明幾個經常使用名稱定義:緩存
線程、主內存和工做內存的交互關係如上圖所示,和CPU-緩存-內存很相似。 不一樣線程之間沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要在主內存來完成。 最後注意,爲了得到較好的執行性能,Java內存模型並無限制執行引擎使用處理器的寄存器或者高速緩存來提高指令執行速度,也沒有限制編譯器對指令進行重排序。也就是說,在java內存模型中,也會存在緩存一致性問題和指令重排序的問題。安全
關於主內存與工做內存之間的具體交互協議,即一個變量如何從主內存拷貝到工做內存、如何從工做內存同步到主內存之間的實現細節,Java內存模型定義瞭如下八種操做來完成:多線程
因此變量讀寫包含如下幾個步驟:併發
注意,Java內存模型只要求上述操做必須按順序執行,而沒有保證必須是連續執行。也就是read和load之間,store和write之間是能夠插入其餘指令的。 除了定義以上8中原子操做,Java內存模型還規定了上述8種基本操做在執行時必須知足必定的操做規則,例如如不容許read和load單獨出現(即不容許一個變量從主內存中讀取但工做內存不接受),不容許store和write單獨出現(即不容許從工做內存中發起了回寫單主內存不接受),這裏不一一列舉,詳細網上搜索便可。 Java內存模型還定義了volatile型變量的特殊規則(下一節介紹),以上三種規定共同肯定了Java中哪些內存訪問操做是安全的即:jvm
8種原子操做+操做規則+volatile規定=Java中哪些內存訪問操做是安全的
當一個變量被定義爲volatile後,將具有兩種特性:
可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。 普通的共享變量不能保證可見性,由於普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。 可是,須要注意的是volatile變量只保證可見性,可是java裏面的運算並不是所有都是原子操做例如++操做,這樣一樣致使volatile修飾變量java運算不安全。 通常不符合如下兩條規則的運算場景中,咱們須要經過加鎖(synchronized或併發包中的鎖)保證變量原子性:
常見的volatile修飾變量的場景是用來做爲開關控制併發:
重排序:是指「編譯器和處理器」爲了提升性能,而在程序執行時會對程序進行的重排序。大體能夠分爲如下三類:
下面咱們看下jvm如何實現volatile禁止指令重排序的:
最後,關於volatile禁止重排序幾點使用說明:
總的來講JMM內存模型是圍繞着在併發過程當中如何處理原子性、可見性和有序性三個特徵來創建的。下面就三個特徵分別說明:
原子性:即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。 java內存模型的read、load、assign、use、store和write六個操做直接保證原子性,咱們能夠任務基本數據類型訪問讀寫是具備原子性(特殊說明long double64位操做根據jvm實現有關)。 若是場景中須要大範圍的原子性保證,java內存模型提供了lock和unlock操做來知足,對應到java代碼關鍵字便是——synchronized。
可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。 除了上面介紹的volatile外,java還提供了兩個關鍵字實現可見性,synchronized和final。
有序性:即程序執行的順序按照代碼的前後順序執行。 java中自然有序性能夠總結爲一句話:若是在本線程內觀察,全部的操做都是有序的;若是在一個線程中觀察另一個線程,全部操做都是無序的。前半句是指「線程內表現爲串行語義」,後半句表示「指令重排」和「工做內存與主內存同步延遲」現象。 java提供了volatile和synchronized兩個關鍵字來保證線程之間操做的有序性,這裏synchronized則是有「同一時刻只容許一條線程對其進行lock操做」這條操做規定獲取的,這個規則決定了同一個鎖的兩個同步塊只能串行進入。
最後,能夠發現synchronized關鍵字能夠同時解決上述三個問題,固然這個須要付出代價就是性能問題。
《深刻理解java虛擬機》——周志明 www.cnblogs.com/dolphin0520…