CAS底層原理萬字示例+詳解衫画!

概念

CAS的全稱是Compare-And-Swap睡榆,它是cpu并發(fā)原語

它的功能是判斷內(nèi)存某個位置的值是否為預(yù)期值场航。如果是則更改為新的值缠导,這個過程是原子的CAS并發(fā)原語體現(xiàn)在java語言中就是sun.misc.Unsafe類的各個方法。調(diào)用UnSafe類中的CAS方法溉痢,JVM會幫我們實現(xiàn)出CAS匯編指令僻造,這是一種完全依賴于硬件的功能,通過它實現(xiàn)了原子操作孩饼,再次強調(diào)髓削,由于CAS是一種系統(tǒng)原語,原語屬于操作系統(tǒng)用于范疇镀娶,是由若干條指令組成立膛,用于完成某個功能的一個過程,并且原語的執(zhí)行必須是連續(xù)的梯码,在執(zhí)行過程中不允許被中斷宝泵,也就是說CAS是一條CPU的原子指令,不會造成所謂的數(shù)據(jù)不一致的問題轩娶,也就是說CAS是線程安全的儿奶。

代碼使用

首先使用AtomicInteger創(chuàng)建了一個實例,并初始化為5

// 創(chuàng)建一個原子類
AtomicInteger atomicInteger= new AtomicInteger(5) 

然后調(diào)用CAS方法鳄抒,企圖更新成2019闯捎,這里有兩個參數(shù)搅窿,一個是5,表示期望值隙券,第二個就是我們要更新的值

atomicInteger.compareAndSet(5,2019) 

然后再次使用了一個方法男应,同樣將值改為1024

atomicInteger.compareAndSet(5,1024) 

完整代碼如下:

public class CASDemo {
    public static void main(String[] args) {
        // 創(chuàng)建一個原子類
        AtomicInteger atomicInteger = new AtomicInteger(5);

        /**
         * 一個是期望值,一個是更新值娱仔,但期望值和原來的值相同時沐飘,才能夠更改
         * 假設(shè)三秒前,我拿的是5牲迫,也就是expect為5耐朴,然后我需要更新成 2019
         */
        System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data: " + atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t current data: " + atomicInteger.get());
    }
} 

上面代碼的執(zhí)行結(jié)果為:

這是因為我們執(zhí)行第一個的時候,期望值和原本值是滿足的盹憎,因此修改成功筛峭,但是第二次后,主內(nèi)存的值已經(jīng)改成了2019陪每,不滿足期望值影晓,因此返回了false,本次寫入失敗

這個就類似于SVN或者Git的版本號檩禾,如果沒有人更改過挂签,就能夠正常提交,否者需要先將代碼pull下來盼产,合并代碼后饵婆,然后提交

CAS底層原理

首先我們先看看atomicInteger.getAndIncrement()方法的源碼

從這里能夠看到,底層又調(diào)用了一個unsafe類的getAndAddInt方法

unsafe類

Unsafe是CAS的核心類戏售,由于Java方法無法直接訪問底層系統(tǒng)侨核,需要通過本地(Native)方法來訪問,Unsafe相當(dāng)于一個后門灌灾,基于該類可以直接操作特定的內(nèi)存數(shù)據(jù)搓译,Unsafe類存在sun.misc包中,其內(nèi)部方法操作可以像C指針一樣直接操作內(nèi)存紧卒,因為Java中的CAS操作的執(zhí)行依賴于Unsafe類的方法侥衬。

注意Unsafe類的所有方法都是native修飾的,也就是說unsafe類中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)的任務(wù)

為什么Atomic修飾的包裝類跑芳,能夠保證原子性轴总,依靠的就是底層的unsafe類

變量valueOffset

表示該變量值在內(nèi)存中的偏移地址,因為Unsafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的

從這里我們可以看到博个,通過valueOffset怀樟,直接通過內(nèi)存地址,獲取到值盆佣,然后進(jìn)行加1操作

變量value用volatile修飾

保證了多線程之間的內(nèi)存可見性

var5:就是我們從主內(nèi)存中拷貝到工作內(nèi)存中的值

那么操作的時候往堡,需要比較工作內(nèi)存中的值械荷,和主內(nèi)存中的值進(jìn)行比較

假設(shè)執(zhí)行 compareAndSwapInt返回false,那么就一直執(zhí)行 while方法虑灰,直到期望的值和真實值一樣

  • val1:AtomicInteger對象本身
  • var2:該對象值得引用地址
  • var4:需要變動的數(shù)量
  • var5:用var1和var2找到的內(nèi)存中的真實值
    • 用該對象當(dāng)前的值與var5比較
    • 如果相同吨瞎,更新var5 + var4 并返回true
    • 如果不同,繼續(xù)取值然后再比較穆咐,直到更新完成

這里沒有用synchronized颤诀,而用CAS,這樣提高了并發(fā)性对湃,也能夠?qū)崿F(xiàn)一致性崖叫,是因為每個線程進(jìn)來后,進(jìn)入的do while循環(huán)拍柒,然后不斷的獲取內(nèi)存中的值心傀,判斷是否為最新,然后在進(jìn)行更新操作拆讯。

假設(shè)線程A和線程B同時執(zhí)行g(shù)etAndInt操作(分別跑在不同的CPU上)

  1. AtomicInteger里面的value原始值為3脂男,即主內(nèi)存中AtomicInteger的 value 為3,根據(jù)JMM模型往果,線程A和線程B各自持有一份價值為3的副本疆液,分別存儲在各自的工作內(nèi)存
  2. 線程A通過getIntVolatile(var1 , var2) 拿到value值3,這是線程A被掛起(該線程失去CPU執(zhí)行權(quán))
  3. 線程B也通過getIntVolatile(var1, var2)方法獲取到value值也是3陕贮,此時剛好線程B沒有被掛起,并執(zhí)行了compareAndSwapInt方法潘飘,比較內(nèi)存的值也是3肮之,成功修改內(nèi)存值為4,線程B打完收工卜录,一切OK
  4. 這是線程A恢復(fù)戈擒,執(zhí)行CAS方法,比較發(fā)現(xiàn)自己手里的數(shù)字3和主內(nèi)存中的數(shù)字4不一致艰毒,說明該值已經(jīng)被其它線程搶先一步修改過了筐高,那么A線程本次修改失敗,只能夠重新讀取后在來一遍了丑瞧,也就是在執(zhí)行do while
  5. 線程A重新獲取value值柑土,因為變量value被volatile修飾,所以其它線程對它的修改绊汹,線程A總能夠看到稽屏,線程A繼續(xù)執(zhí)行compareAndSwapInt進(jìn)行比較替換,直到成功西乖。

Unsafe類 + CAS思想: 也就是自旋狐榔,自我旋轉(zhuǎn)

底層匯編

Unsafe類中的compareAndSwapInt是一個本地方法坛增,該方法的實現(xiàn)位于unsafe.cpp中

  • 先想辦法拿到變量value在內(nèi)存中的地址
  • 通過Atomic::cmpxchg實現(xiàn)比較替換,其中參數(shù)X是即將更新的值薄腻,參數(shù)e是原內(nèi)存的值

CAS缺點

CAS不加鎖收捣,保證一次性,但是需要多次比較

  • 循環(huán)時間長庵楷,開銷大(因為執(zhí)行的是do while罢艾,如果比較不成功一直在循環(huán),最差的情況嫁乘,就是某個線程一直取到的值和預(yù)期值都不一樣昆婿,這樣就會無限循環(huán))
  • 只能保證一個共享變量的原子操作
    • 當(dāng)對一個共享變量執(zhí)行操作時,我們可以通過循環(huán)CAS的方式來保證原子操作
    • 但是對于多個共享變量操作時蜓斧,循環(huán)CAS就無法保證操作的原子性仓蛆,這個時候只能用鎖來保證原子性
  • 引出來ABA問題?

ABA問題

假設(shè)兩個線程T1和T2訪問同一個變量V挎春,當(dāng)T1訪問變量V時看疙,讀取到V的值為A;此時線程T1被搶占了直奋,T2開始執(zhí)行能庆,T2先將變量V的值從A變成B,然后又將變量V的值從B變成A脚线;此時T1又搶占了主動權(quán)搁胆,繼續(xù)執(zhí)行,它發(fā)現(xiàn)V的值還是A邮绿,以為沒有變化渠旁,所以就繼續(xù)執(zhí)行了。這個過程中船逮,變量V從A變?yōu)锽顾腊,再由B變?yōu)锳就形象的稱為ABA問題。
總結(jié)
--

CAS

CAS是compareAndSwap挖胃,比較當(dāng)前工作內(nèi)存中的值和主物理內(nèi)存中的值杂靶,如果相同則執(zhí)行規(guī)定操作,否者繼續(xù)比較直到主內(nèi)存和工作內(nèi)存的值一致為止

CAS應(yīng)用

CAS有3個操作數(shù)酱鸭,內(nèi)存值V吗垮,舊的預(yù)期值A(chǔ),要修改的更新值B凛辣。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時抱既,將內(nèi)存值V修改為B,否者什么都不做

補充

參考:https://blog.csdn.net/longgeqiaojie304/article/details/89819316

1扁誓、CAS底層是匯編

Unsafe類中的compareAndSwapInt防泵,是一個本地方法蚀之,該方法的實現(xiàn)位于unsafe.cpp中

2、CAS會導(dǎo)致ABA問題

CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某個時刻的數(shù)據(jù)并在當(dāng)下時刻比較并替換捷泞,那么在這個時間差內(nèi)會導(dǎo)致數(shù)據(jù)的變化足删。

比如說一個線程one從內(nèi)存位置V中取出A,這個時候另一個線程two也從內(nèi)存位置V中取出A锁右,并且線程two進(jìn)行了一些操作將值變成了B失受,然后線程two又將V位置的數(shù)據(jù)變成A,這時候線程one進(jìn)行CAS操作時發(fā)現(xiàn)內(nèi)存中仍然是A咏瑟,然后線程one操作成功拂到。

盡管線程one的CAS操作成功,但是并不代表這個過程就是沒有問題的码泞。

作者:柒
鏈接:https://www.cnblogs.com/qiuwenli/p/13516505.html
來源:cnblogs

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兄旬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子余寥,更是在濱河造成了極大的恐慌领铐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宋舷,死亡現(xiàn)場離奇詭異绪撵,居然都是意外死亡,警方通過查閱死者的電腦和手機祝蝠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門音诈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绎狭,你說我怎么就攤上這事改艇。” “怎么了坟岔?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摔桦。 經(jīng)常有香客問我社付,道長,這世上最難降的妖魔是什么邻耕? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任鸥咖,我火速辦了婚禮,結(jié)果婚禮上兄世,老公的妹妹穿的比我還像新娘啼辣。我一直安慰自己,他們只是感情好御滩,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布鸥拧。 她就那樣靜靜地躺著党远,像睡著了一般。 火紅的嫁衣襯著肌膚如雪富弦。 梳的紋絲不亂的頭發(fā)上沟娱,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音腕柜,去河邊找鬼济似。 笑死,一個胖子當(dāng)著我的面吹牛盏缤,可吹牛的內(nèi)容都是我干的砰蠢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼唉铜,長吁一口氣:“原來是場噩夢啊……” “哼台舱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起打毛,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤柿赊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幻枉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碰声,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年熬甫,在試婚紗的時候發(fā)現(xiàn)自己被綠了胰挑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡椿肩,死狀恐怖瞻颂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郑象,我是刑警寧澤贡这,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站厂榛,受9級特大地震影響盖矫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜击奶,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一辈双、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柜砾,春花似錦湃望、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞳浦。三九已至,卻和暖如春檩帐,著一層夾襖步出監(jiān)牢的瞬間术幔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工湃密, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诅挑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓泛源,卻偏偏與公主長得像拔妥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子达箍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容