1. 程式人生 > 其它 >從多執行緒模型覆盤volatile

從多執行緒模型覆盤volatile

從多執行緒模型覆盤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字首指令的作用:

  1. 將快取行重新整理回主存
  2. 保證lock字首指令之前的指令都完成,並且實現了記憶體屏障的效果,禁止重排序

volatile的執行緒中特性的保障

經過上面的分析就能夠得出volatile做出的保障:

  1. 原子性(單個指令)
  2. 有序性,lock字首指令乾的(記憶體屏障
  3. 可見性,lock字首指令和MESI模型乾的

討論一下為什麼volatile實現不了原子性

就拿最簡單也最常見的場景來說- i ++

i ++ 就是兩步操作,1.讀取i; 2.將i設定成i + 1;

  1. A執行緒讀取到了i=1,它知道自己要將i設定為2
  2. B執行緒此時也讀取到了i=1,它知道自己要將i設定為2
  3. 然後就出現問題了,兩個執行緒都將會把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的特性