CAS

1什么是CAS

????CAS(Compare And Swap),即比較并交換,是解決多線(xiàn)程并行情況下使用鎖造成性能損耗的一種機(jī)制。

????CAS機(jī)制當(dāng)中使用了3個(gè)基本操作數(shù):內(nèi)存地址V,舊的預(yù)期值A(chǔ),要修改的新值B。
更新一個(gè)變量的時(shí)候,只有當(dāng)變量的預(yù)期值A(chǔ)和內(nèi)存地址V當(dāng)中的實(shí)際值相同時(shí),才會(huì)將內(nèi)存地址V對(duì)應(yīng)的值修改為B。

1.1在內(nèi)存地址V當(dāng)中,存儲(chǔ)著值為10的變量。

1.2此時(shí)線(xiàn)程1想要把變量的值增加1。對(duì)線(xiàn)程1來(lái)說(shuō),舊的預(yù)期值A(chǔ)=10,要修改的新值B=11。


1.3在線(xiàn)程1要提交更新之前,另一個(gè)線(xiàn)程2搶先一步,把內(nèi)存地址V中的變量值率先更新成了11。

1.4線(xiàn)程1開(kāi)始提交更新,首先進(jìn)行A和地址V的實(shí)際值比較(Compare),發(fā)現(xiàn)A不等于V的實(shí)際值,提交失敗。


1.5線(xiàn)程1重新獲取內(nèi)存地址V的當(dāng)前值,并重新計(jì)算想要修改的新值。此時(shí)對(duì)線(xiàn)程1來(lái)說(shuō),A=11,B=12。這個(gè)重新嘗試的過(guò)程被稱(chēng)為自旋。


1.6這一次比較幸運(yùn),沒(méi)有其他線(xiàn)程改變地址V的值。線(xiàn)程1進(jìn)行Compare,發(fā)現(xiàn)A和地址V的實(shí)際值是相等的。

image.png

1.7線(xiàn)程1進(jìn)行SWAP,把地址V的值替換為B,也就是12。

從思想上來(lái)說(shuō),Synchronized屬于悲觀(guān)鎖,悲觀(guān)地認(rèn)為程序中的并發(fā)情況嚴(yán)重,所以嚴(yán)防死守。CAS屬于樂(lè)觀(guān)鎖,樂(lè)觀(guān)地認(rèn)為程序中的并發(fā)情況不那么嚴(yán)重,所以讓線(xiàn)程不斷去嘗試更新。

在CAS中,比較和替換是一組原子操作,不會(huì)被外部打斷,且在性能上更占有優(yōu)勢(shì)。

Unsafe提供了三個(gè)方法用于CAS操作

public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);

public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);

public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);

解析AtomicInteger

當(dāng)我們使用 AtomicInteger 實(shí)現(xiàn)多線(xiàn)程的加操作時(shí),分析源碼

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;

   //帶參數(shù)的構(gòu)造函數(shù)
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
   //缺省構(gòu)造函數(shù)
    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

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

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

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

    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

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

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

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

    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }

    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }

    public String toString() {
        return Integer.toString(get());
    }

    public int intValue() {
        return get();
    }

    public long longValue() {
        return (long)get();
    }

    public float floatValue() {
        return (float)get();
    }

    public double doubleValue() {
        return (double)get();
    }

}

解析

  • 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ù)。

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

  • 變量value用volatile修飾,保證了多線(xiàn)程之間的內(nèi)存可見(jiàn)性. 我們?cè)谑褂胏as更新value的時(shí)候,沒(méi)有用到volatile,但get、set方法獲取value時(shí)用到了volatile的可見(jiàn)性。這樣結(jié)合來(lái)看使得AtomicInteger類(lèi)獲取的是主存中最新的值 且 更新時(shí)cas是原子性操作。

  • 假設(shè)線(xiàn)程A和線(xiàn)程B同時(shí)執(zhí)行g(shù)etAndAdd操作, 因?yàn)楸容^與替換是原子性操作,所以即使在var5獲取到相同的值,也會(huì)是順序的更新。

CAS缺點(diǎn)

  • ABA問(wèn)題。

如果在這段期間曾經(jīng)被改成B,然后又改回A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被修改過(guò)。針對(duì)這種情況,java并發(fā)包中提供了一個(gè)帶有標(biāo)記的原子引用類(lèi) AtomicStampedReference,它可以通過(guò)控制變量值的版本來(lái)保證CAS的正確性, 該類(lèi)提供一個(gè)引用和計(jì)數(shù)變量。

  • 循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大。自旋CAS如果長(zhǎng)時(shí)間不成功,會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo)。

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

當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí),我們可以使用循環(huán)CAS的方式來(lái)保證原子操作,但是對(duì)多個(gè)共享變量操作時(shí),循環(huán)CAS就無(wú)法保證操作的原子性。

從Java1.5開(kāi)始JDK提供了 AtomicReference 類(lèi)來(lái)保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行CAS操作。

AtomicReference與AtomicInteger的不同

AtomicInteger是對(duì)整數(shù)的封裝,底層采用的是compareAndSwapInt實(shí)現(xiàn)CAS,比較的是數(shù)值是否相等。
AtomicReference則對(duì)應(yīng)普通的對(duì)象引用,底層使用的是compareAndSwapObject實(shí)現(xiàn)CAS,比較的是兩個(gè)對(duì)象的地址是否相等。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。

小例子

public class Main{
    public static void main(String[] args) {
 
        // 創(chuàng)建兩個(gè)Person對(duì)象,它們的id分別是101和102。
        Person2 p1 = new Person2(101);
        Person2 p2 = new Person2(102);
        // 新建AtomicReference對(duì)象,初始化它的值為p1對(duì)象
        AtomicReference ar = new AtomicReference(p1);
        //更改p1的id.
        p1.setId(106);
        // 通過(guò)CAS設(shè)置ar。如果ar的值為p1的話(huà),則將其設(shè)置為p2。
        ar.compareAndSet(p1, p2);
 
        Person2 p3 = (Person2)ar.get();
        System.out.println("p3 is "+p3);
        System.out.println("p3.equals(p1)="+p3.equals(p1));
    }
 
}
class Person2 {
    volatile long id;
    public Person2(long id) {
        this.id = id;
    }
    public String toString() {
        return "id:"+id;
    }
    public void setId(long id){
        this.id=id;
    }
}

運(yùn)行結(jié)果如下:

p3 is id:102
p3.equals(p1)=false

總結(jié)

修改了p1的id對(duì)compareAndSet()并沒(méi)有影響,因?yàn)樾薷膇d僅僅改變了p1的成員變量,而AtomicReference底層使用的是compareAndSwapObject實(shí)現(xiàn)CAS,比較的是兩個(gè)對(duì)象的地址是否相等。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。上面代碼成功的將原子類(lèi)中的引用從p1變成p2,而且是線(xiàn)程安全的。

參考文獻(xiàn)

https://www.cnblogs.com/princessd8251/articles/5187403.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容