從多執行緒模型覆盤volatile
阿新 • • 發佈:2022-11-29
從多執行緒模型覆盤volatile
volatile的存在,我猜就是為了適應JMM這種執行緒之間有本地記憶體和有共享記憶體的模型。解決了資料的可見性問題和有序性問題。
volatile的效果
當兩個執行緒使用到了同一個變數的時候,他們都會在自己執行緒的本地記憶體中有一個變數的副本,而這個副本是自己獨享的,所以對其的所有操作是執行緒間隔離的。volatile的效果就是讓他們不再被隔離。
MESI模型
MESI:
M:modified 被修改了 E:exclusived 被獨佔的 S:shared 被共享的 I:isolated 被隔離的
當一個執行緒對被volatile修飾的變數A進行修改的時候,會將其快取中的快取行設定為M,表示被修改了,然後這時候嗅探機制發揮作用,其他執行緒進行感知,將其原本的S修改為I。之後再從這個I的快取行讀取的時候,就會從主存重新整理。
volatile的底層原理
volatile的原理在如今比較簡單,x86架構下就是lock字首指令去保障著volatile的。
lock字首指令的作用:
將快取行重新整理回主存 保證lock字首指令之前的指令都完成,並且實現了記憶體屏障的效果,禁止重排序
volatile的執行緒中特性的保障
經過上面的分析就能夠得出volatile做出的保障:
原子性(單個指令) 有序性,lock字首指令乾的(記憶體屏障 可見性,lock字首指令和MESI模型乾的
討論一下為什麼volatile實現不了原子性
就拿最簡單也最常見的場景來說- i ++
i ++ 就是兩步操作,1.讀取i; 2.將i設定成i + 1;
A執行緒讀取到了i=1,它知道自己要將i設定為2 B執行緒此時也讀取到了i=1,它知道自己要將i設定為2 然後就出現問題了,兩個執行緒都將會把i設定為2.
要解決這麼一種對於共享變數的獲取的情況,就讓我們的加鎖來做,或者說用作業系統給我們提供出來的CAS原子操作。
CAS
CAS也叫compare and swap,他會對記憶體中的值和它儲存的值進行對比,如果發現和它儲存的值一樣才會進行修改,這種操作是原子性的。
如上圖所示,如果有了CAS之後,我們就可以有這麼一種符合一致性的結果。
CAS的底層原理
首先CAS一定是底層提供出來的一個原子操作,但我們看到的CAS是經過了封裝後的,要在JVM層面實現的CAS,為了適應JMM模型也有一些改變,比如它也要有可見性和有序性。 如下圖,CAS在JVM中的實現是Atomic::cmpxchg。 它會先判斷我們的當前機器有無多核,如果有多核的話,他會為我們新增上lock字首指令,讓我們能夠保持有序性和可見性。 之後的就是參照底層的方法提供引數。
CAS + 自旋 實現樂觀鎖
CAS的問題
ABA問題
由於CAS只會進行比較值,所以它沒辦法感知在它讀和寫的這段時間內,有無人對值進行了操作,最終變回了原值。
ABA的解決就是進行對版本的感知,加入版本號這類的東西。如AtomicStampedReference就進行了實現,每一次的修改都會改動版本號。
CAS自旋對CPU的佔用問題
這就是一個本身用機器效能換效率的事情,無法解決
只能對單個變數進行操作
這也是CAS的特性