CAS原理

1、什麼是CAS?java

比較並交換(compare and swap),它是一條CPU併發原語。判斷內存某個位置的值是否爲預期值,若是是的話則更新爲新值,這個過程是原子的。多線程

原語是操做系統的用語,是由若干條指定組成,用於完成某個功能的一個過程,而且原語的執行必須是連續的,在執行過程當中不容許被中斷,也就是說CAS是一條CPU的原子指令,不會形成所謂的數據不一致問題。併發

public class Solution
{
    public static void main(String[] args)
    {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,33)+ ". current data is: " + atomicInteger.get());
    }
}

運行結果:this

true. current data is: 22
false. current data is: 22
public class Solution
{
    public static void main(String[] args)
    {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(22,33)+ ". current data is: " + atomicInteger.get());
    }
}

運行結果:atom

true. current data is: 22
true. current data is: 33

結果說明:spa

一、第一次運行,因爲在物理內存中的值是5,因此符合expected的值,就修改成了22,而第二條語句的expected爲5,可是如今值已經爲22,因此不符合預期,就不能改變值。操作系統

二、第二次運行,每次的expected的值都爲物理內存中的值,因此都進行了修改。線程

2、CAS底層原理,什麼是UnSafe?指針

要點:UnSafe類(存在rt.jar中)+CAS自旋鎖code

AtomicInteger的源碼

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
 
    // 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;
}

一、Unsafe類

這是CAS的核心類,因爲Java方法沒法直接訪問底層系統,須要經過本地(native)方法來訪問,UnSafe至關於一個後門,基於該類能夠直接操做特定內存的數據

UnSafe類存在於sun.misc包中,其內部方法操做能夠像C的指針同樣直接操做內存,所以Java中CAS操做執行依賴於UnSafe類

UnSafe類中的全部方法都是native修飾的,也就是說UnSafe類中的方法都直接調用操做系統底層資源執行相應任務

二、變量valueOffset,表示該變量在內存中的偏移地址,由於UnSafe就是根據內存偏移地址獲取數據的。

//this:當前對象
//valueOffset:內存偏移量
 public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
 }
//var1 AtomicInteger對象自己
//var2 該對象值的引用地址
//var4 須要變更的值
//var5 用var1 var2找出主內存中真實的值
//用該對象當前值與var5比較,若是相同,更新var5+var4返回true,若是不一樣,繼續取值比較,直到更新完成
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;
}  

總結:getAndIncrement()底層調用unsafe類方法,傳入三個參數,unsafe.getAndAddInt()底層使用CAS思想,若是比較成功加1,若是比較失敗則從新得到,再比較,直到成功

三、變量value用volatile修飾,保證了多線程之間的內存可見性

2、CAS缺點?

一、循環時間開銷大(若是CAS失敗,會一直嘗試)

2、只能保證一個共享變量的原子操做。(對多個共享變量操做時,循環CAS沒法保證操做的原子性,只能用加鎖來保證)

三、存在ABA問題

4、原子類AtomicInteger類ABA問題及解決方案

一、ABA問題是怎麼產生的?

當第一個線程執行CAS(V,E,U)操做,在獲取到當前變量V,準備修改成新的值U以前。另外兩個線程已經連續修改了兩次變量V的值,使得該值又恢復爲舊的值,這樣咱們就沒法正確判斷這個變量是否已經被修改過

二、ABA問題的解決方案:

AtomicStampedReference:是一個帶有時間戳的對象引用,在每次修改後,不只會設置新的值,還會記錄修改的時間

AtomicMarkableReference:維護的是一個Boolean值的標識,這種方式並不能徹底防止ABA問題的發生,只能減小ABA發生的機率

public class Solution
{
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) throws InterruptedException
    {
        System.out.println("========ABA問題的產生=========");
        new Thread(() ->
        {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() ->
        {
            try
            {
                TimeUnit.SECONDS.sleep(1);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019) + " " + atomicReference.get());
        }, "t2").start();
        TimeUnit.SECONDS.sleep(2);

        System.out.println("========ABA問題的解決=========");

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

        }, "t3").start();

        new Thread(() ->
        {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "線程第1次版本號:" + stamp);
            try
            {
                TimeUnit.SECONDS.sleep(3);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "修改爲功否:" + result + "  當前最新版本號:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "當前最新值:" + atomicStampedReference.getReference());

        }, "t4").start();
    }
}

運行結果:

========ABA問題的產生=========
true 2019
========ABA問題的解決=========
t3線程第1次版本號:1
t4線程第1次版本號:1
t3線程第2次版本號:2
t3線程第3次版本號:3
t4修改爲功否:false  當前最新版本號:3
t4當前最新值:100

5、原子更新引用

class User
{
    User(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
}

public class Solution
{
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) throws InterruptedException
    {
        AtomicReference<User> atomicReference = new AtomicReference<>();

        User user = new User("monster", 18);
        User updateUser = new User("jack", 25);

        atomicReference.set(user);
        System.out.println(atomicReference.get());
        atomicReference.compareAndSet(user, updateUser);
        System.out.println(atomicReference.get());
    }
}

運行結果:

User@74a14482
User@1540e19d
相關文章
相關標籤/搜索