【面試專欄】一文了解CAS

文章同步更新在個人公眾號“梓莘”敬察,歡迎大家關注,相互交流尔当。

一莲祸、什么是CAS

CompareAndSwap,比較當前工作內(nèi)存中的值和主內(nèi)存中的值,如果相同則執(zhí)行規(guī)定操作椭迎,否則繼續(xù)比較直到主內(nèi)存中的值一致為止锐帜。
CAS有3個操作數(shù),內(nèi)存值為V畜号,舊的預期值為A缴阎,要修改的更新值為B。當且僅當預期值A和內(nèi)存值V相同時简软,將內(nèi)存值V修改為B蛮拔,否則什么都不做述暂。

Demo

package com.zishen;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName CASDemo
 * @Description TODO
 * @Author zishen
 * @Date 2019/12/25 8:06
 * @Version 1.0
 * 1、什么是CAS
 *     比較并交換
 **/
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        //如果內(nèi)存值和期望值相同則修改
        System.out.println(atomicInteger.compareAndSet(5,2019)+"\t current data is :"+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,1024)+"\t current data is :"+atomicInteger.get());

    }
}

分析

5-CAS分析.png

由上面我們可以看到他實際上調(diào)用的Unsafe類的方法建炫。接下來看下Unsafe.java

二畦韭、Unsafe

  1. Unsafe是CAS的核心類,由于Java方法無法直接訪問底層系統(tǒng)肛跌,需要通過本地(native)方法來訪問艺配,Unsafe相當于開了一個后門,基于該類可以直接操作特定內(nèi)存的數(shù)據(jù)衍慎。UnSafe類存在于sun.misc包中转唉,其內(nèi)部方法操作可以像C的指針一樣直接操作內(nèi)存,因為Java中CAS操作的執(zhí)行依賴于UnSafe類的方法稳捆。
    注意:Unsafe類的所有方法都是native修飾的赠法,也就是說UnSafe類中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應任務。
  2. 變量valueOffset,表示該變量值在內(nèi)存中的偏移地址眷柔,因為UnSafe就是根據(jù)內(nèi)存偏移地址來獲取數(shù)據(jù)的期虾。
  3. 變量value用volatile修飾,保證了多線程之間的內(nèi)存可見性

三驯嘱、換個姿勢重新看

CAS的全稱為Compare-And-Swap,從操作系統(tǒng)層面來看镶苞,它是一條CPU并發(fā)原語。

它的功能是判斷內(nèi)存某個位置的值是否為預期值鞠评,如果是則更改為新的值茂蚓,這個過程是原子的。

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ù)不一致問題般渡。

放碼過來

public final int getAndAddInt(Object paramObject, long paramLong, int paramInt)
  {
    int i;
    do
      i = getIntVolatile(paramObject, paramLong);
    while (!(compareAndSwapInt(paramObject, paramLong, i, i + paramInt)));
    return i;
  }

  • paramObject:AtomicInteger對象本身
  • paramLong:該對象值的引用地址
  • paramInt:需要變動的數(shù)量
  • i:是通過paramObject和paramLong找出的主內(nèi)存中真實的值

用該對象當前的值與i進行比較

  • 如果相同,更新paramInt+i并且返回true
  • 如果不同,繼續(xù)取值然后再比較驯用,直到更新完成脸秽。

舉個栗子

假設線程A和線程B兩個線程同時執(zhí)行getAndAddInt操作(分別跑在不同的cpu上)

  1. AtomicInteger里面的value原始值為3,即主內(nèi)存中AtomicInteger的value為3晨汹,根據(jù)JMM模型豹储,線程A和線程B各自持有一份值為3的value的副本分為到各自的工作內(nèi)存。
  2. 線程A通過getIntVolatile(var1,var2)拿到value的值為3淘这,這時線程A被掛起
  3. 線程B也通過geyIntVolatile(var1 ,var2)方法獲取到value值為3剥扣,此時剛好線程B沒有被掛起并執(zhí)行compareAndSwapInt方法,此時比較內(nèi)存值也為3铝穷,成功修改內(nèi)存值為4钠怯,線程B執(zhí)行完畢。
  4. 這時線程A恢復曙聂,執(zhí)行compareAndSwapInt方法比較晦炊,發(fā)現(xiàn)自己手里的值數(shù)字3和主內(nèi)存的值4數(shù)字不一致,說明該值已經(jīng)被其他現(xiàn)場搶先一步修改過了宁脊,那A線程本次修改失敗断国,只能重新獲取重新來一次。
  5. 線程A重新獲取value值榆苞,因為變量value被volatile修飾稳衬,所以其他線程對它的修改,線程A總是能夠看到坐漏,線程A繼續(xù)執(zhí)行compareAndSwapInt進行比較替換薄疚,直到成功。

四赊琳、更上一層樓

UnSafe類中的compareAndSwapInt是一個本地方法街夭,該方法的實現(xiàn)位于unsafe.cpp中(可以看:http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/tip/src/share/vm/prims/unsafe.cpp),

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

先想辦法拿到value在內(nèi)存中的地址躏筏,通過Atomic::cmpxchg實現(xiàn)比較替換板丽,其中參數(shù)x是即將更新的值,參數(shù)e是原內(nèi)存的值趁尼。

五檐什、CAS的缺點

  1. 循環(huán)時間長開銷大
    在getAndAddInt方法執(zhí)行時,有一個do-while弱卡,如果CAS失敗,會一直進行嘗試住册,如果CAS長時間一直不成功婶博,可能會給CPU帶來很大的開銷。
  2. 只能保證一個共享變量的原子操作
  3. 引出來ABA問題

書寫不易荧飞,轉載請注明出處凡人。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末名党,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挠轴,更是在濱河造成了極大的恐慌传睹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸晦,死亡現(xiàn)場離奇詭異欧啤,居然都是意外死亡,警方通過查閱死者的電腦和手機启上,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門邢隧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冈在,你說我怎么就攤上這事倒慧。” “怎么了包券?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵纫谅,是天一觀的道長。 經(jīng)常有香客問我溅固,道長付秕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任发魄,我火速辦了婚禮盹牧,結果婚禮上,老公的妹妹穿的比我還像新娘励幼。我一直安慰自己汰寓,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布苹粟。 她就那樣靜靜地躺著有滑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嵌削。 梳的紋絲不亂的頭發(fā)上毛好,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音苛秕,去河邊找鬼肌访。 笑死,一個胖子當著我的面吹牛艇劫,可吹牛的內(nèi)容都是我干的吼驶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蟹演!你這毒婦竟也來了风钻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤酒请,失蹤者是張志新(化名)和其女友劉穎骡技,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羞反,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡布朦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了苟弛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喝滞。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖膏秫,靈堂內(nèi)的尸體忽然破棺而出右遭,到底是詐尸還是另有隱情,我是刑警寧澤缤削,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布窘哈,位于F島的核電站,受9級特大地震影響亭敢,放射性物質(zhì)發(fā)生泄漏滚婉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一帅刀、第九天 我趴在偏房一處隱蔽的房頂上張望让腹。 院中可真熱鬧,春花似錦扣溺、人聲如沸骇窍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腹纳。三九已至,卻和暖如春驱犹,著一層夾襖步出監(jiān)牢的瞬間嘲恍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工雄驹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留佃牛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓医舆,卻偏偏與公主長得像吁脱,于是被迫代替她去往敵國和親桑涎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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