Java技術專題-原始碼分析系列(1)volatile工作原理分析

語言: CN / TW / HK

一、前提概要

1、用過這個關鍵字的童鞋都知道,都知道這個關鍵字很強大,主要作用是保證變數在多執行緒之間的可見性;
2、volatile在concurrent包中起著舉足輕重的作用,為大量的併發類提供了有力的援助;
3、接下來我們從瞭解CPU快取開始,然後再深入原理剖析,循序漸進的瞭解volatile;

二、CPU快取

2.1 傳輸鏈路

CPU(執行緒) 
--》  CPU快取(一級、二級、三級快取等)  
--》  主記憶體

大致的傳輸方向就這樣,而且還是必須是雙向傳輸。

2.2 CPU快取

1、CPU快取解決了CPU運算速度與記憶體讀者速度不匹配的問題;

2、因為主記憶體訪問通常比較慢,訪問時間大概在幾十到幾百個時鐘,而CPU快取還有一二三級之分,每個級別的讀者速度雖然很快,
   但還是有訪問速度的區分,至少比主記憶體的讀者速度快很多,所以CPU快取它的出現在很大程度上提高了資料之間的傳輸;

3、每一級快取中所儲存的資料全部都是下一級快取中的一部分,這三種快取的技術難度和製造成本是相對遞減的,所以其容量也相對遞增;

4、當CPU要讀取一個數據時,首先從一級快取中查詢,如果沒有再從二級快取中查詢,如果還是沒有再從三級快取中或記憶體中查詢。
   一般來說每級快取的命中率大概都有80%左右,也就是說全部資料量的80%都可以在一級快取中找到;

三、原理特性

3.1、可見性

1、淺顯的講,不論執行緒是如何如何的訪問帶volatile欄位的物件,都會訪問到記憶體中最新的一份值,這就是可見性的大致闡述;

2、具體的講,當我們在java程式碼中書寫的那行對volatile物件進行寫操作時,JVM會向處理器傳送一條Lock指令,
   Lock指令鎖住(鎖匯流排)確保變數物件所在快取行資料會更新到主記憶體中去,確保更新後如果再有其他執行緒訪問該物件,
   其他執行緒一律強制從主記憶體中重新讀取最新的值。

3、因為所有記憶體的傳輸都發生在一條共享的總線上,並且所有的處理器都能看到這條匯流排,那麼既然所有處理器都能看到這條匯流排,
   總不至於看見了不幹點啥吧?

4、沒錯,每個處理器都會通過一種監聽技術,不停的監聽總線上傳輸的資料,以便來檢查自己快取中的資料是否過期。
   當處理器發現快取記憶體中的資料對應的記憶體地址被修改,會將該快取資料置為失效,當處理器下次訪問該記憶體地址
   資料時,將強制重新從系統記憶體中讀取。

5、而且CPU製造商也曾制定了一個這樣的規則:當一個CPU修改快取中的位元組物件時,伺服器中其他CPU會被通知,它們的快取將視為無效。
   當那些被視為無效變數所在的執行緒再次訪問位元組物件時,則強制再次從主記憶體中獲取最新值。

6、至於第2點提到Lock鎖匯流排,其實最初採用鎖匯流排,雖說能解決問題,但是效率地下,一旦鎖匯流排,其他CPU就得乾等著,
   光看不幹效率不行嘛。所以後來優化成了鎖快取,效率也高了,開銷也自然就少了,總之Lock目的很明確,確保鎖住的那份值最新,
   且其他持有該快取的備份處都得失效,其實這種鎖快取過程的思想也正是快取一致性協議的核心思想。

7、綜上所述,所以不論何時不論何地在哪種多執行緒環境下,只要你想獲取被volatile修飾過的欄位,都能看到最新的一份值,
   這就是可見性的終極描述。

3.2、有序性

1、淺顯的講,A1,A2,A3三塊程式碼先後執行,A2有一行程式碼被volatile修飾過,那麼在被反編譯成指令進行重排序時,A2必須等到A1
   執行完了才能開始,但是A1內部的指令可以支援重排指令;而A3程式碼塊的執行必須等到A2執行完了才能開始,但是A3內部的指令可以支援
   重排指令,這就是有序性,只要A2夾在中間,A2必須等A1執行完才能幹活,A2沒幹完活,A3是不允許開工的。

2、具體的講,Lock字首指令實際上相當於一個記憶體屏障(也成記憶體柵欄),它確保指令重排序時不會把其後面的指令排到記憶體屏障之前的位置,
   也不會把前面的指令排到記憶體屏障的後面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成。

3、綜上所述,有序不是我們通常說的自然順序,而是在有volatile修飾時,存在類似尊卑等級的先後有序這麼一說。

3.3、非原子性

1、本不該拿到檯面上講是不是屬於volatile的特性,因為我們不能認為僅僅只是因為可見性隨處都是最新值,那麼就認為是原子性操作。

2、對於第1種想法,簡直是大錯特錯,因為可見性只是將volatile變數這回主記憶體並使得其他CPU快取失效,但是不帶代表對volatile變數
   回寫主記憶體的動作和對volatile變數的邏輯操作是捆綁在一起的。因此既要邏輯操作,又要寫回主記憶體,這本來就違背了volatile特性
   的本意,所以volatile並不是原子操作的。
分享到:
「其他文章」