Compare and Swap

什么是CAS

是一種思想,是一種實現(xiàn)線程安全的算法莺匠,同時也是一條CPU指令,比如Compare and Swap這一條指令就能完成“比較并交換”原子操作。

CAS有三個操作數(shù):內(nèi)存值V阳掐、預期值A、要修改的值B冷蚂,當且晉檔預期值A和內(nèi)存值V相同時缭保,才將內(nèi)存值修改為B,否則什么都不做蝙茶。最后返回現(xiàn)在的V值艺骂。
用大白話說就是:我任務V的值應該是A,如果是的話那我就把它改成B隆夯,如果不是A(說明被別人修改過了)钳恕,那我就不修改了。
這種思想最終通過調(diào)用CPU的一些特殊指令來實現(xiàn)蹄衷,這些指令由CPU保證了“比較和交換”兩個動作的原子性苞尝。

分析在java中是如何利用CAS的CPU指令實現(xiàn)“比較和交換”原子操作

讓我們來看看AtomicInteger是如何通過CAS實現(xiàn)并發(fā)下的累加操作的,以AtomicInteger的getAndAdd方法為突破口宦芦。

getAndAdd方法

public final int getAndAdd(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta);
}

可以看出宙址,這里使用了Unsafe這個類,這里需要簡要介紹一下Unsafe類:

Unsafe類

Unsafe是CAS的核心類调卑。Java無法直接訪問底層操作系統(tǒng)抡砂,而是通過本地(native)方法來訪問。不過盡管如此恬涧,JVM還是開了一個后門注益,JDK中有一個類Unsafe,它提供了硬件級別的原子操作溯捆。

AtomicInteger加載Unsafe工具丑搔,用來直接操作內(nèi)存數(shù)據(jù)

public class AtomicInteger extends Number implements java.io.Serializable {
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    public final int get() {
        return value;
    }

在AtomicInteger數(shù)據(jù)定義的部分厦瓢,我們還獲取了unsafe實例,并且定義了valueOffset啤月。再看到static塊煮仇,懂類加載過程的都知道,static塊的加載發(fā)生于類加載的時候谎仲,是最先初始化的浙垫,這時候我們調(diào)用unsafe的objectFieldOffset從Atomic類文件中獲取value的偏移量,那么valueOffset其實就是記錄value值在內(nèi)存中的偏移地址郑诺。
有了value在工作內(nèi)存中的偏移地址之后夹姥,我們就可以用unsafe直接操作這個地址了,通過這個地址我們可以獲取原值辙诞,也可以寫入新值辙售。
因為是修改工作內(nèi)存中的value值,所以value是用volatile修飾的飞涂,保證了多線程之間不會出現(xiàn)可見性的問題旦部。

接下來繼續(xù)看Unsafe的getAndAddInt方法的實現(xiàn)

// AtomicInter類
public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
}
// Unsafe類
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
}

我們看var5獲取的是什么,通過調(diào)用unsafe的getIntVolatile(var1, var2)封拧,這是個native方法志鹃,其實就是獲取var1中,var2偏移量處的值泽西。var1就是AtomicInteger曹铃,var2就是我們前面提到的valueOffset,這樣我們就從內(nèi)存里獲取到現(xiàn)在valueOffset處的值了。

現(xiàn)在重點來了捧杉,compareAndSwapInt(var1, var2, var5, var5 + var4)其實換成compareAndSwapInt(obj, offset, expect, update)比較清楚陕见,意思就是如果obj內(nèi)的value和expect相等,就證明沒有其他線程改變過這個變量味抖,那么就更新它為update评甜,如果這一步的CAS沒有成功,那就采用自旋的方式繼續(xù)進行CAS操作仔涩。

加法意味著只要在最新的值上加上我要加的值即可忍坷,我先取出工作內(nèi)存中最新的值(有volatile保證最新),然后進行“比對并交換”原子操作熔脂。比對成功說明我剛從工作內(nèi)存中取出的值是最新的佩研,可以往該值加上我要加的值。比對不成功霞揉,說明我剛剛取出工作內(nèi)存中的值的確是最新的旬薯,但不幸過后就被別的線程修改了,我比對的這一刻已經(jīng)不是最新的了适秩,所以我不能往該值加上我要加的值(加的話會導致加少)绊序,只能再次去工作內(nèi)存中獲取最新的值再加上硕舆,不釋放CPU,也就是在自旋骤公。
-->自旋+cas = atomicInteger

Unsafe類中的compareAndSwapInt本地方法

compareAndSwapInt.cpp

根據(jù)偏移量拿到value值在工作內(nèi)存中的地址
通過Atomic:cmpxchg實現(xiàn)原子性的比較和替換抚官,其中x是即將更新的值,e是原內(nèi)存的值淋样。

CAS思想在別的地方的體現(xiàn)

  • 在數(shù)據(jù)庫更新操作中耗式,更新語句的where條件會帶上一個版本號胁住,若版本號不符合趁猴,則不進行更新操作。隨后再select出最新的版本號彪见,再嘗試去進行更新操作儡司。如此反復。
并發(fā)容器concurrenthashmap的put操作

缺點

  • ABA問題
    可以通過版本號來解決
  • 競爭激烈時余指,自旋時間可能會過長
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捕犬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酵镜,更是在濱河造成了極大的恐慌碉碉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淮韭,死亡現(xiàn)場離奇詭異垢粮,居然都是意外死亡,警方通過查閱死者的電腦和手機靠粪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門蜡吧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人占键,你說我怎么就攤上這事昔善。” “怎么了畔乙?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵君仆,是天一觀的道長。 經(jīng)常有香客問我牲距,道長返咱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任嗅虏,我火速辦了婚禮洛姑,結果婚禮上,老公的妹妹穿的比我還像新娘皮服。我一直安慰自己楞艾,他們只是感情好参咙,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硫眯,像睡著了一般蕴侧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上两入,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天净宵,我揣著相機與錄音,去河邊找鬼裹纳。 笑死择葡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的剃氧。 我是一名探鬼主播敏储,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朋鞍!你這毒婦竟也來了已添?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤滥酥,失蹤者是張志新(化名)和其女友劉穎更舞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坎吻,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡缆蝉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了禾怠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片返奉。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吗氏,靈堂內(nèi)的尸體忽然破棺而出芽偏,到底是詐尸還是另有隱情,我是刑警寧澤弦讽,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布污尉,位于F島的核電站,受9級特大地震影響往产,放射性物質發(fā)生泄漏被碗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一仿村、第九天 我趴在偏房一處隱蔽的房頂上張望锐朴。 院中可真熱鬧,春花似錦蔼囊、人聲如沸焚志。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酱酬。三九已至壶谒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膳沽,已是汗流浹背汗菜。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挑社,地道東北人陨界。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像滔灶,于是被迫代替她去往敵國和親普碎。 傳聞我的和親對象是個殘疾皇子吼肥,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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