天天看點

volatile關鍵字的深入了解

volatile關鍵字的深入了解

問題引起的緣由:硬體層面的優化帶來的共享資料a可見性問題

盡可能消除CPU與記憶體的速度差異(引入高速緩存) -> 緩存一緻性問題(引入緩存一緻性協定(X86架構用的MESI)) -> 緩存一緻性通信問題(引入storebuffer) -> 重排序問題(引入記憶體屏障)

由于CUP/記憶體/IO/磁盤三者之間的處理速度的差異引發的優化,CPU進行資料運算時需要存取記憶體中的資料,但他們之間的速度差異很懸殊;1、第一步優化是引入了高速緩存,盡可能消除之間的速度差異,分别L1/L2/L3緩存(速度逐級遞減),而每個CPU都有自己的緩存(相當于L1),而多個CPU在通路共享資源時,會出現資料安全性問題,及CPU1改了某個共享資源a的值時,沒有及時同步到主記憶體,而CUP2在計算a的值時不是拿的最新資料(即緩存一緻性問題)。2、是以,這時又引入緩存一緻性協定(X86架構用的MESI)【有四種标記狀态...,在CPU層面做一個監控】(緩存行鎖,鎖的粒度小,隻對共享資料進行加鎖),通過緩存一緻性協定來互相通知到各個CPU中緩存行a的值是不是需要重新到主記憶體 擷取(L2->L3->主記憶體),但緩存一緻性協定會存在一個通信延遲問題,在這中間會存在CPU阻塞的時間片段,這也是CPU資源的浪費,那又怎麼辦呢?3、優秀的工程師又引入了一個storebuffer來實作異步操作,進而進一步進行CPU資源的優化利用,即在改變共享變量a的值之,不需要等待通信結果,可以直接運作下面的程式指令;但這又會引發另一個問題,指令重排序問題(就是在兩個CPU在執行某些場景的代碼時,a變量為來得及同步,其他CPU就使用了a變量的值,類似于代碼亂序執行),而這個問題是在硬體層面無法解決的問題,因為在硬體層面不知道軟體層面何時需要進行這樣的優化,是以會留有一個口子給軟體應用開發者去使用,即CPU層面提供了指令->記憶體屏障(讀屏障、寫屏障、全屏障),加了這個屏障之後,相當于屏蔽了storebuffer的優化(強制性将storebuffer通知其他CPU的緩存行,或者說去除了storebuffer的優化?容忍短暫的阻塞?),即使得a共享變量的可見性。而映射在java層面,則是提供volatile/sychronized/final/happens-before 等關鍵字來進行加記憶體屏障的操作。

疑問:

1、記憶體屏障是相當于去除storebuffer的優化,還是指的去除其他哪些優化?

2、為什麼要引入高速緩存(各個CPU有自己的高速緩存)?這又會引起緩存一緻性問題

  答:盡可能消除CPU與記憶體之間的速度差異(消除短闆)

總結:導緻可見性問題的根本原因是:高速緩存,重排序

引出JMM(java記憶體模型):不同的平台對于記憶體屏障的實作是不一樣的,屏蔽平台的差異性,JMM最核心的目的是解決了可見性和有序性問題

源代碼 -》 編譯器重排序 -》CPU重排序(指令級/記憶體,在不存在依賴關系的情況下,CPU可以優化亂序執行)-》最終執行指令