說(shuō)一說(shuō)CAS

compareAndSet----比較并交換

AtomicInteger.conpareAndSet(int expect, indt update)

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

第一個(gè)參數(shù)為拿到的期望值厚宰,如果期望值沒(méi)有一致涤久,進(jìn)行update賦值,如果期望值不一致袁勺,證明數(shù)據(jù)被修改過(guò),返回fasle咒钟,取消賦值

例子:

package com.jian8.juc.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 1.CAS是什么疏哗?
 * 1.1比較并交換
 */
public class CASDemo {
    public static void main(String[] args) {
       checkCAS();
    }

    public static void checkCAS(){
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data is " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 2014) + "\t current data is " + atomicInteger.get());
    }
}

輸出結(jié)果為:

true     current data is 2019
false    current data is 2019

2、CAS底層原理庐镐?對(duì)Unsafe的理解

比較當(dāng)前工作內(nèi)存中的值和主內(nèi)存中的值蒋伦,如果相同則執(zhí)行規(guī)定操作,否則繼續(xù)比較知道主內(nèi)存和工作內(nèi)存中的值一直為止

  1. atomicInteger.getAndIncrement();

        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }
    
  2. Unsafe

    • 是CAS核心類(lèi)焚鹊,由于Java方法無(wú)法直接訪(fǎng)問(wèn)地層系統(tǒng)痕届,需要通過(guò)本地(native)方法來(lái)訪(fǎng)問(wèn),Unsafe相當(dāng)于一個(gè)后門(mén)末患,基于該類(lèi)可以直接操作特定內(nèi)存數(shù)據(jù)研叫。Unsafe類(lèi)存在于sun.misc包中,其內(nèi)部方法操作可以像C的指針一樣直接操作內(nèi)存璧针,因?yàn)镴ava中CAS操作的執(zhí)行依賴(lài)于Unsafe類(lèi)的方法嚷炉。

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

    • 變量valueOffset探橱,表示該變量值在內(nèi)存中的偏移地址申屹,因?yàn)閁nsafe就是根據(jù)內(nèi)存便宜地址獲取數(shù)據(jù)的

    • 變量value用volatile修飾,保證多線(xiàn)程之間的可見(jiàn)性

  3. CAS是什么

    CAS全稱(chēng)呼Compare-And-Swap隧膏,它是一條CPU并發(fā)原語(yǔ)

    他的功能是判斷內(nèi)存某個(gè)位置的值是否為預(yù)期值哗讥,如果是則更改為新的值,這個(gè)過(guò)程是原子的胞枕。

    CAS并發(fā)原語(yǔ)體現(xiàn)在JAVA語(yǔ)言中就是sun.misc.Unsafe類(lèi)中各個(gè)方法杆煞。調(diào)用Unsafe類(lèi)中的CAS方法,JVM會(huì)幫我們實(shí)現(xiàn)CAS匯編指令腐泻。這是一種完全依賴(lài)于硬件的功能决乎,通過(guò)他實(shí)現(xiàn)了原子操作。由于CAS是一種系統(tǒng)原語(yǔ)派桩,原語(yǔ)屬于操作系統(tǒng)用語(yǔ)范疇构诚,是由若干條指令組成的,用于完成某個(gè)功能的一個(gè)過(guò)程铆惑,并且原語(yǔ)的執(zhí)行必須是連續(xù)的范嘱,在執(zhí)行過(guò)程中不允許被中斷送膳,也就是說(shuō)CAS是一條CPU的原子指令,不會(huì)造成數(shù)據(jù)不一致問(wèn)題彤侍。

    //unsafe.getAndAddInt
        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;
        }
    

    var1 AtomicInteger對(duì)象本身

    var2 該對(duì)象的引用地址

    var4 需要變動(dòng)的數(shù)據(jù)

    var5 通過(guò)var1 var2找出的主內(nèi)存中真實(shí)的值

    用該對(duì)象前的值與var5比較肠缨;

    如果相同,更新var5+var4并且返回true盏阶,

    如果不同晒奕,繼續(xù)去之然后再比較,直到更新完成

3名斟、CAS缺點(diǎn)

  1. ** 循環(huán)時(shí)間長(zhǎng)脑慧,開(kāi)銷(xiāo)大**

    例如getAndAddInt方法執(zhí)行,有個(gè)do while循環(huán)砰盐,如果CAS失敗闷袒,一直會(huì)進(jìn)行嘗試,如果CAS長(zhǎng)時(shí)間不成功岩梳,可能會(huì)給CPU帶來(lái)很大的開(kāi)銷(xiāo)

  2. 只能保證一個(gè)共享變量的原子操作

    對(duì)多個(gè)共享變量操作時(shí)囊骤,循環(huán)CAS就無(wú)法保證操作的原子性,這個(gè)時(shí)候就可以用鎖來(lái)保證原子性

  3. ABA問(wèn)題

三冀值、原子類(lèi)AtomicInteger的ABA問(wèn)題也物?原子更新引用?

1列疗、ABA如何產(chǎn)生

CAS算法實(shí)現(xiàn)一個(gè)重要前提需要去除內(nèi)存中某個(gè)時(shí)刻的數(shù)據(jù)并在當(dāng)下時(shí)刻比較并替換滑蚯,那么在這個(gè)時(shí)間差類(lèi)會(huì)導(dǎo)致數(shù)據(jù)的變化。

比如線(xiàn)程1從內(nèi)存位置V取出A抵栈,線(xiàn)程2同時(shí)也從內(nèi)存取出A告材,并且線(xiàn)程2進(jìn)行一些操作將值改為B,然后線(xiàn)程2又將V位置數(shù)據(jù)改成A古劲,這時(shí)候線(xiàn)程1進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中的值依然時(shí)A斥赋,然后線(xiàn)程1操作成功。

==盡管線(xiàn)程1的CAS操作成功绢慢,但是不代表這個(gè)過(guò)程沒(méi)有問(wèn)題==

2灿渴、如何解決?原子引用

示例代碼:

package juc.cas;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

import java.util.concurrent.atomic.AtomicReference;

public class AtomicRefrenceDemo {
    public static void main(String[] args) {
        User z3 = new User("張三", 22);
        User l4 = new User("李四", 23);
        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
    }
}

@Getter
@ToString
@AllArgsConstructor
class User {
    String userName;
    int age;
}

輸出結(jié)果

true    User(userName=李四, age=23)
false   User(userName=李四, age=23)

3胰舆、時(shí)間戳的原子引用

新增機(jī)制,修改版本號(hào)

package com.jian8.juc.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * ABA問(wèn)題解決
 * AtomicStampedReference
 */
public class ABADemo {
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        System.out.println("=====以下時(shí)ABA問(wèn)題的產(chǎn)生=====");
        new Thread(() -> {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "Thread 1").start();

        new Thread(() -> {
            try {
                //保證線(xiàn)程1完成一次ABA操作
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
        }, "Thread 2").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=====以下時(shí)ABA問(wèn)題的解決=====");

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第1次版本號(hào)" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t第2次版本號(hào)" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t第3次版本號(hào)" + atomicStampedReference.getStamp());
        }, "Thread 3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第1次版本號(hào)" + stamp);
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);

            System.out.println(Thread.currentThread().getName() + "\t修改是否成功" + result + "\t當(dāng)前最新實(shí)際版本號(hào):" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "\t當(dāng)前最新實(shí)際值:" + atomicStampedReference.getReference());
        }, "Thread 4").start();
    }
}

輸出結(jié)果:

=====以下時(shí)ABA問(wèn)題的產(chǎn)生=====
true    2019
=====以下時(shí)ABA問(wèn)題的解決=====
Thread 3    第1次版本號(hào)1
Thread 4    第1次版本號(hào)1
Thread 3    第2次版本號(hào)2
Thread 3    第3次版本號(hào)3
Thread 4    修改是否成功false 當(dāng)前最新實(shí)際版本號(hào):3
Thread 4    當(dāng)前最新實(shí)際值:100
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹬挤,一起剝皮案震驚了整個(gè)濱河市缚窿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌焰扳,老刑警劉巖倦零,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件误续,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扫茅,警方通過(guò)查閱死者的電腦和手機(jī)蹋嵌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)葫隙,“玉大人,你說(shuō)我怎么就攤上這事∈榫郏” “怎么了抹锄?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)糟描。 經(jīng)常有香客問(wèn)我怀喉,道長(zhǎng),這世上最難降的妖魔是什么船响? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任躬拢,我火速辦了婚禮,結(jié)果婚禮上见间,老公的妹妹穿的比我還像新娘聊闯。我一直安慰自己,他們只是感情好缤剧,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布馅袁。 她就那樣靜靜地躺著,像睡著了一般荒辕。 火紅的嫁衣襯著肌膚如雪汗销。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,821評(píng)論 1 314
  • 那天抵窒,我揣著相機(jī)與錄音弛针,去河邊找鬼。 笑死李皇,一個(gè)胖子當(dāng)著我的面吹牛削茁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掉房,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼茧跋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卓囚?” 一聲冷哼從身側(cè)響起瘾杭,我...
    開(kāi)封第一講書(shū)人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哪亿,沒(méi)想到半個(gè)月后粥烁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贤笆,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年讨阻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芥永。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钝吮,死狀恐怖埋涧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搀绣,我是刑警寧澤飞袋,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站链患,受9級(jí)特大地震影響巧鸭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜麻捻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一纲仍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贸毕,春花似錦郑叠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至摊腋,卻和暖如春沸版,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兴蒸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工视粮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人橙凳。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓蕾殴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親岛啸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钓觉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

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

  • 一、線(xiàn)程狀態(tài)轉(zhuǎn)換新建(New)可運(yùn)行(Runnable)阻塞(Blocking)無(wú)限期等待(Waiting)限期等...
    達(dá)微閱讀 588評(píng)論 1 2
  • 為了感謝支持我的朋友坚踩!整理了一份Java高級(jí)架構(gòu)資料议谷、Spring源碼分析、Dubbo堕虹、Redis卧晓、Netty、z...
    Java耕耘者閱讀 857評(píng)論 0 0
  • 有人說(shuō)商業(yè)世界如狗咬狗一般競(jìng)爭(zhēng)激烈赴捞,每個(gè)人都必須奮力攀登到頂峰逼裆。但是,在工作中赦政,公平公開(kāi)的競(jìng)爭(zhēng)產(chǎn)生的成功和狡猾卑鄙...
    斯坦威閱讀 672評(píng)論 0 1
  • 一級(jí)標(biāo)題 二級(jí)標(biāo)題 三級(jí)標(biāo)題 四級(jí)標(biāo)題 五級(jí)標(biāo)題 六級(jí)標(biāo)題 這是里引用, 引用的用法 無(wú)序列表 1 2 3 1 2...
    墨如白閱讀 260評(píng)論 0 0
  • 韶苑碧樹(shù)柯丫胜宇, 獨(dú)無(wú)嬌艷梨花。 不禁東風(fēng)淚灑恢着, 嚴(yán)冬烈夏桐愉, 與誰(shuí)共翥天涯??
    梅姿閱讀 1,416評(píng)論 38 50