03 原子操作CAS(Compare And Swap)

1 什么是原子操作?如何實現(xiàn)原子操作推励?

2 CAS的原理

在計算機科學(xué)中蜻懦,比較和交換(Conmpare And Swap)是用于實現(xiàn)多線程同步的原子指令夸溶。 它將內(nèi)存位置的內(nèi)容與給定值進行比較,只有在相同的情況下损肛,將該內(nèi)存位置的內(nèi)容修改為新的給定值厢破。 這是作為單個原子操作完成的。 原子性保證新值基于最新信息計算; 如果該值在同一時間被另一個線程更新治拿,則寫入將失敗摩泪。 操作結(jié)果必須說明是否進行替換; 這可以通過一個簡單的布爾響應(yīng)(這個變體通常稱為比較和設(shè)置),或通過返回從內(nèi)存位置讀取的值來完成(摘自維基本科)

  • CAS(Compare And Swap)劫谅,指令級別保證這是一個原子操作
  • 三個運算符: 一個內(nèi)存地址V见坑,一個期望的值A(chǔ),一個新值B
  • 基本思路:如果地址V上的值和期望的值A(chǔ)相等捏检,就給地址V賦給新值B荞驴,如果不是,不做任何操作贯城。
  • 循環(huán)(死循環(huán)熊楼,自旋)里不斷的進行CAS操作

3 JDK提供的CAS操作類

  • 更新基本類型類:AtomicBoolean,AtomicInteger能犯,AtomicLong鲫骗,AtomicReference
  • 更新數(shù)組類:AtomicIntegerArray,AtomicLongArray悲雳,AtomicReferenceArray
  • 更新引用類型:AtomicReference挎峦,AtomicMarkableReference,AtomicStampedReference
  • 原子更新字段類: AtomicReferenceFieldUpdater合瓢,AtomicIntegerFieldUpdater坦胶,AtomicLongFieldUpdater

4 CAS在JUC中的運用

JUC中非常重要的一個類AbstractQueuedSynchronizer,作為JAVA中多種鎖實現(xiàn)的父類晴楔,其中有很多地方使用到了CAS操作以提升并發(fā)的效率.
此段源碼是往queue隊列中添加node節(jié)點顿苇,利用了自旋和CAS原理

/**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

上面為同步隊列的入隊操作,也是一種樂觀鎖的實現(xiàn)税弃,多線程情況下纪岁,操作頭節(jié)點和尾節(jié)點都有可能失敗,失敗后會再次嘗試则果,直到成功幔翰。

5 CAS的問題

5.1 ABA問題

什么是ABA問題
如線程1從內(nèi)存X中取出A漩氨,這時候另一個線程2也從內(nèi)存X中取出A,并且線程2進行了一些操作將內(nèi)存X中的值變成了B遗增,然后線程2又將內(nèi)存X中的數(shù)據(jù)變成A叫惊,這時候線程1進行CAS操作發(fā)現(xiàn)內(nèi)存X中仍然是A,然后線程1操作成功做修。雖然線程1的CAS操作成功霍狰,但是整個過程就是有問題的。比如鏈表的頭在變化了兩次后恢復(fù)了原值饰及,但是不代表鏈表就沒有變化蔗坯。
解決方法

  • 使用版本號
    ABA問題的解決思路是使用版本號,每次變量更新的時候版本號加1燎含,那么A->B->A就會變成1A->2B->3A
  • jdk自帶原子變量
    從jdk1.5開始宾濒,jdk的Atomic包里就提供了一個類AtomicStampedReference來解決ABA問題,這個類中的compareAndSet方法的作用就是首先檢查當(dāng)前引用是否等于預(yù)期引用瘫镇,并且檢查當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志鼎兽,如果全部相等,則以原子方式將該引用和該標(biāo)志的值更新為指定的新值
/**
     * 如果當(dāng)前引用等于預(yù)期引用并且當(dāng)前標(biāo)志等于預(yù)期標(biāo)志
     * 則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定新值
     *
     * @param expectedReference 預(yù)期引用值
     * @param newReference 新的引用值
     * @param expectedStamp 預(yù)期標(biāo)記值
     * @param newStamp 新標(biāo)記值
     * @return {@code true} if successful
     */
    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
        #預(yù)期引用==當(dāng)前引用
            expectedReference == current.reference &&
            #預(yù)期標(biāo)志==當(dāng)前標(biāo)志
            expectedStamp == current.stamp &&
            #新引用==當(dāng)前引用 并且 新標(biāo)志==當(dāng)前標(biāo)志
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
              #原子更新值
             casPair(current, Pair.of(newReference, newStamp)));
    }

5.2 開銷問題

循環(huán)時間長開銷大
自旋CAS如果長時間不成功铣除,會給CPU帶來非常大的執(zhí)行開銷谚咬。如果jvm能支持處理器提供的pause指令,那么效率會有一定的提升尚粘。pause指令有兩個作用:

第一择卦,它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會消耗過多的執(zhí)行資源郎嫁,延遲的時間取決于具體實現(xiàn)的版本秉继,在一些處理器上延遲時間是零。

第二泽铛,它可以避免在退出循環(huán)的時候因內(nèi)存順序沖突(Memory Order Violation)而引起CPU流水線被清空(CPU Pipeline Flush)尚辑,從而提高CPU的執(zhí)行效率。

5.3 只能保證一個共享變量的原子操作

CAS機制所保證的只是一個變量的原子性操作盔腔,而不能保證整個代碼塊的原子性杠茬。比如需要保證3個變量共同進行原子性的更新,就不得不使用Synchronized了弛随。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓢喉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子舀透,更是在濱河造成了極大的恐慌栓票,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愕够,死亡現(xiàn)場離奇詭異走贪,居然都是意外死亡佛猛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門厉斟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挚躯,“玉大人,你說我怎么就攤上這事擦秽。” “怎么了漩勤?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵感挥,是天一觀的道長。 經(jīng)常有香客問我越败,道長触幼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任究飞,我火速辦了婚禮置谦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亿傅。我一直安慰自己媒峡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布葵擎。 她就那樣靜靜地躺著谅阿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酬滤。 梳的紋絲不亂的頭發(fā)上签餐,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音盯串,去河邊找鬼氯檐。 笑死,一個胖子當(dāng)著我的面吹牛体捏,可吹牛的內(nèi)容都是我干的冠摄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼译打,長吁一口氣:“原來是場噩夢啊……” “哼耗拓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奏司,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤乔询,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后韵洋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竿刁,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡黄锤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了食拜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸵熟。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖负甸,靈堂內(nèi)的尸體忽然破棺而出流强,到底是詐尸還是另有隱情,我是刑警寧澤呻待,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布打月,位于F島的核電站,受9級特大地震影響蚕捉,放射性物質(zhì)發(fā)生泄漏奏篙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一迫淹、第九天 我趴在偏房一處隱蔽的房頂上張望秘通。 院中可真熱鬧,春花似錦敛熬、人聲如沸肺稀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盹靴。三九已至,卻和暖如春瑞妇,著一層夾襖步出監(jiān)牢的瞬間稿静,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工辕狰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留改备,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓蔓倍,卻偏偏與公主長得像悬钳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子偶翅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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