深刻理解java虛擬機【併發編程緩存】

  隨着多核CPU的高速發展,爲了充分利用硬件的計算資源,操做系統的併發多任務功能正變得愈來愈重要,可是CPU在進行計算時,還須要從內存讀取輸出,並 將計算結果存放到內存中,然而因爲CPU的運算速度比內存高几個數量級,CPU內的寄存器數量和容量有限,爲了避免讓CPU長時間處於等待內存的空閒狀態, 在CPU和內存之間引入了速度接近CPU的高速緩存Cache做爲CPU和內存之間的緩衝。計算機硬件併發的原理以下:

java

Java虛擬機對併發的支持相似於計算機硬件,java虛擬機的併發支持是經過java虛擬機的內存模型來實現的。Java虛擬機的內存模型分爲主內存和 工做內存,程序中全部的變量都存儲在主內存中,每一個線程有本身的私有工做內存,工做內存中保存了被該線程使用到的變量的主內存拷貝,線程對變量的全部操做 (讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量,不一樣線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞須要經過 主內存來完成。Java虛擬機併發原理以下:緩存

  Java虛擬機內存模型中定義了8種關於主內存和工做內存的交互協議操做:安全

(1).lock鎖定:做用於主內存的變量,把一個變量標識爲一條線程獨佔狀態。併發

(2).unlock解鎖:做用於主內存的變量,把一個處於鎖定狀態的變量釋放出來,釋放後的變量能夠被其餘線程鎖定。優化

(3).read讀取:做用於主內的變量,把一個變量的值從主內存傳輸到線程的工做內存中,以便隨後的load動做使用。spa

(4).load加載:做用於工做內存的變量,把read讀取操做從主內存中獲得的變量值放入工做內存的變量拷貝中。操作系統

(5).use使用:做用於工做內存的變量,把工做內存中一個變量的值傳遞給java虛擬機執行引擎,每當虛擬機遇到一個須要使用到變量值的字節碼指令時將會執行該操做。線程

(6).assign賦值:做用於工做內存變量,把一個從執行引擎接收到的變量的值賦值給工做變量,每當虛擬機遇到一個給變量賦值的字節碼時將會執行該操做。排序

(7).store存儲:做用於工做內存的變量,把工做內存中一個變量的值傳送到主內存中,以便隨後的write操做使用。內存

(8).write寫入:做用於主內存的變量,把store操做從工做內存中獲得的變量值放入主內存的變量中。


  Java內存模型對上述8種操做有以下的約束:

(1).把一個變量從主內存複製到工做內存中必須順序執行read讀入操做和load載入操做。

把一個變量從工做內存同步回主內存中必須順序執行store存儲操做和write寫入操做。

read和load操做之間、store和write操做之間能夠插入其餘指令,可是read和load操做、store和write操做必需要按順序執行,即不容許read和load、store和write操做之一單獨出現。

(2).不容許一個線程丟棄它的最近的assign賦值操做,即工做內存變量值改變以後必須同步回主內存。只有發生過assign賦值操做的變量才須要從工做內存同步回主內存。

(3).一個新變量只能在主內存中產生,不容許在工做內存中直接使用一個未被初始化(load或assign)的變量,即一個變量在進行use和store操做以前,必須先執行過assgin和load操做。

(4).一個變量在同一時刻只容許一條線程對其進行lock鎖定操做,可是lock鎖定能夠被一條線程重複執行屢次,屢次執行lock以後,只有執行相同次數的unlock操做變量纔會被解鎖。

(5).若是對一個變量執行lock鎖定操做,將會清空工做內存中該變量的值,在執行引擎使用這個變量前,須要從新執行load或assign操做初始化變量的值。

(6).若是一個變量事先沒有被lock鎖定,則不容許對這個變量進行unlock解鎖操做,也不容許對一個被別的線程鎖定的變量進行unlock解鎖。

(7).一個變量進行unlock解鎖操做以前,必須先把此變量同步回主內存中(執行store和write操做)。


  Java中的關鍵字volatile是java虛擬機提供的最輕量級的線程同步機制,當一個變量被聲明爲volatile以後,該變量將具有如下兩種特性:

(1).volatile保證變量對全部線程的可見性,即任何一個線程修改了該變量的值以後,新值對於全部其餘線程都是能夠當即得知的。

而普通變量須要先將工做內存中的變量同步回主內存,其餘線程都須要從主內存從新讀取變量的值才能使用最新修改後的值。

volatile變量也能夠在各個工做內存中存在不一致的狀況,但因爲每次使用以前都須要先刷新(工做內存變量從新執行初始化),執行引擎看不到變量不一致的狀況,所以能夠任務volatile變量不存在不一致的狀況。

可是java中的運算並不是所有都是原子操做,所以volatile變量的運行在併發下同樣是線程不安全的。

因爲volatile變量只能保證可見性,只有在符合以下兩條規則狀況纔是線程安全的。

a.運算結果不依賴變量的當前值,或者可以確保只有單一線程修改變量的值。

b.變量不須要與其餘其餘變量共同參與不變約束。

不符合上述兩條規則狀況下,仍然須要經過synchronized同步關鍵字或者加鎖機制來保證線程安全。

(2).volatile禁止指令重排序優化。

普通變量僅能保證在方法執行過程當中全部依賴賦值結果的地方都能獲取正確的結果,而沒法保證變量賦值操做順序與程序代碼執行順序一致。

volatile禁止指令重排序,所以volatile變量的約束以下:

a.volatile變量的操做必須按read->load->use順序,即每次在工做內存中使用變量前必須先從主內存中刷新最新的值,以保證能看到其餘線程對變量的最新修改。

b. volatile變量的操做必須按assign->store->write順序,即每次在工做內存爲變量賦值以後必須將變量的值同步回主內存,以保證讓其餘線程能看到變量的最新修改。

c.若線程對volatile變量A的assign或者use操做先於對volatile變量B的assign或者use操做,則線程對volatile 變量A的read/load或者store/write操做也一定先於對volatile變量B的read/load或者store/write操做。

相關文章
相關標籤/搜索