CAS思想以及相關的問題

1.概念:CAS的全稱爲Compare-And-Swap,是一條CPU併發原語
它的功能是判斷內存某個位置的值是否爲預期值,如果是則更改爲新的值,這個過程是原子的。
示例:
[外鏈圖片轉存失敗(img-1BTErb5g-1563082507378)(en-resource://database/626:0)]

CAS併發原語體現在JAVA的sun.misc.Unsafe類中的各個方法。調用 UnSafe類中的CAS方法,JVM會幫我們實現出CAS彙編範疇。這是一種完全依賴於硬件的操作。

2.CAS底層思想

觀察AtomicInteger中的getAndIncrement()方法,
在這裏插入圖片描述

變量valueOffset表示在內存中偏移地址value被volatile關鍵字修飾,保證了可見性

這個方法就是藉助UnSafe類中的getAndAddInt()方法實現的。
再進入UnSafe類內部,找到getAndAddInt()方法,
在這裏插入圖片描述解釋:
在這裏插入圖片描述

3.CAS的缺點:
1)某些情況下可能會導致循環時間過長,致使cpu開銷過大;
2)只能保證一個共享變量的原子操作;
3)ABA問題

何爲ABA問題??
解釋:⼀箇舊值A變爲了成B,然後再變成A,剛好在做CAS時檢查發現舊值並沒有變化依然爲A,但是實際上的確發生了變化。
例如,一個線程1從主內存位置V處找到A,並拷貝一份A回自己的工作內存中,此時另一個線程2也從主內存位置V處取到A,也拷貝了一份A回自己的工作內存中,線程2進行了一些操作將值變成了B,然後線程2又將主內存V位置處的數據變成A,這時候線程1進行CAS操作時發現內存中的值仍然是A,然後線程1操作成功。也就是說主內存A中的值並沒有發生根本的改變。

那麼如何解決ABA問題呢?

使用AtomicStampedReference(時間戳原子引用),類似於數據庫中常用的樂觀鎖的方式,引入一個版本號來解決。
驗證代碼:

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


public class ABADemo {

    //普通原子引用
    private static AtomicReference<Integer> atomicInteger = new AtomicReference<>(100);
    private static AtomicStampedReference<Integer> atomicStampedReference =
            new AtomicStampedReference<>(100,1);


    public static void main(String[] args) {
        System.out.println("=========以下是ABA的產生================");

        new Thread(()->{
           boolean flag1 = atomicInteger.compareAndSet(100, 101);
           boolean flag2 = atomicInteger.compareAndSet(101, 100);

            System.out.println(flag1+",current data:"+atomicInteger.get());
            System.out.println(flag2+",current data:"+atomicInteger.get());


        },"t1").start();

        new Thread(()->{
            try{
                //保證上面的t1線程,保證t1線程完成了一次CAS操作
                TimeUnit.SECONDS.sleep(1);
                System.out.println(atomicInteger.compareAndSet(100,2019)+"\t"+atomicInteger.get());
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        },"t2").start();

        //暫停2秒
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("=========以下是ABA的解決================");

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() +","+stamp+"\t第1次版本號:"+stamp);

            //暫停1秒鐘t3線程
            try {
                TimeUnit.SECONDS.sleep(1);
                atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),
                        atomicStampedReference.getStamp()+1);
                System.out.println(Thread.currentThread().getName() +","+stamp+"\t第2次版本號:"+atomicStampedReference.getStamp());
                atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),
                        atomicStampedReference.getStamp()+1);
                System.out.println(Thread.currentThread().getName() +","+stamp+"\t第3次版本號:"+atomicStampedReference.getStamp());
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        },"t3").start();


        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() +","+stamp+"\t首次版本號:"+stamp);

            //暫停3秒鐘t4線程,保證t3線程完成一次CAS操作
            try {
                TimeUnit.SECONDS.sleep(3);
                boolean res = atomicStampedReference.compareAndSet(100,2019,
                        stamp,stamp+1);

                System.out.println(Thread.currentThread().getName() +"\t修改成功否:"+res
                        +"\t當前最新實際版本號:"+atomicStampedReference.getStamp());

                System.out.println(Thread.currentThread().getName()+"\t當前實際最新值:"+atomicStampedReference.getReference());
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        },"t4").start();


    }
}


運行結果:
在這裏插入圖片描述
此時,已經很好地解決了ABA問題在實際開發中給我們帶來的不便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章