volatile作用與處理器嗅探的簡解


先貼一下 volatile 的作用定義

如果一個字段被聲明成volatile,Java線程內存模型確保所有線程看到這個變量的值是一致的

首先問題就來了,一個共享變量再被volatile修飾過后,怎么被確保所有線程看到的這個變量的值是一致的的呢,也就是說volatile是如何來保證可見性的呢?

在X86處理器下通過工具獲取JIT編譯器生成的匯編指令來查看對volatile進行寫操作時,CPU會做什么事情。

private volatile instance = new Singleton(); 

轉變成匯編代碼,如下。

0x01a3de1d: movb $0×0,0×1104800(%esi);

0x01a3de24: lock addl $0×0,(%esp);

有 volatile 變量修飾的共享變量進行寫操作的時候會多出第二行匯編代碼,通過查IA-32架構軟件開發者手冊可知,Lock前綴的指令在多核處理器下會引發了兩件事情。

1)將當前處理器緩存行的數據寫回到系統內存。
2)這個寫回內存的操作會使在其他CPU里緩存了該內存地址的數據無效。

第一件事很容易理解,處理器在修改數據后通常都是先寫入到緩存中,但是並不會第一時間寫回到主內存中。

被 volatile 修飾后,這個變量被操作后會立即被寫回到主內存中。(當然整個過程會比較復雜,但是我們只需要從結果上來看和簡化理解就OK了。)

那第二件事中這個寫回內存的操作是如何使其他CPU里緩存了該內存地址的數據無效的呢?

為了提高處理速度,處理器不直接和內存進行通信,而是先將系統內存的數據讀到內部緩存(L1,L2或其他)后再進行操作,但操作完不知道何時會寫到內存。
如果對聲明了volatile的變量進行寫操作,JVM就會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫回到系統內存。
但是,就算寫回到內存,如果其他處理器緩存的值還是舊的,再執行計算操作就會有問題。
所以,在多處理器下,為了保證各個處理器的緩存是一致的,就會實現緩存一致性協議,每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存的值是不是過期了,
當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存里。

這么看來的話,各處理器之間是通過實現緩存一致性協議來完成第二件事的。

那么“每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存的值是不是過期了”,這里的處理器是如何嗅探的呢?

我百度了半天也沒有單獨將如何嗅探的,但是看了一遍博文后(並發研究之CPU緩存一致性協議(MESI)) ,我有了一些自己的理解,當然也只是為了簡化復雜流程的簡化理解。

 

 

以上是目前流行的多級緩存結構簡化圖,

處理器無論是想要加載數據或者寫回數據,都需要通過總線(圖中的②bus)來傳播,

那么我們也就可以將 “處理器通過嗅探在總線上傳播的數據” 這樣的操作形象的理解為處理器監聽 總線 上的所有修改操作,

當處理器發現自己的緩存中的某個數據在總線上被其他的處理器修改了,那么就將自己緩存中的這個數據的狀態變成無效狀態,

然后當處理器在處理這個無效狀態的數據時,會重新去主內存中加載這個數據,然后在進行相應的操作。

比如:CPU A的cache a 已經緩存了 x,然后 CPU B的cache b 也已經緩存了 x,這時 CPU A要修改 x 的值,

然后先將修改后的數據寫回到主內存中,在寫回到主內存的同時被CPU B嗅探到了,並且發現這個數據在自己的cache b中也存在,然后CPU B就先將自己cache b中的 x 的狀態設置成無效,

當CPU B處理到 x 時,發現 x 的狀態是無效的,就只能先去主內存中重新加載 x 的值后再操作。

 

以上便是個人對於處理器嗅探操作的簡化理解,雖然簡化理解后的流程順序和原本的流程順序有所出入,但是這樣簡化理解只是為了方便自己理解和記憶。

需要了解具體的整體操作流程的話,可以去看上面提到的那邊博文。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM