Java併發編程學習(7)---原子操作CAS

目錄頁:https://blog.csdn.net/u011294519/article/details/88367808

1. Compare and Swap [CAS]   

1.1. 小聲嗶嗶

    之前學習的synchronized本質上是一種悲觀鎖,雖然的確安全有效,但是卻犧牲了性能,原因大家也能想明白,等待鎖的線程啥也幹不了,直到獲取鎖。這也是HashTable不被待見的原因,翻看源碼會發現一堆的synchronized(扯遠了)。

    那麼問題來了,要啥自行車。Java爲我們提供了CAS算法,一種樂觀鎖,我們希望在不受干擾的情況下進行更新操作,但是事實往往會有本山叔來要我們的自行車,CAS這種方法會進行衝突檢測來判斷更新期間是否有其他的干擾,以防被忽悠瘸了,在衝突檢測中,如果發現操作失敗我們可以選擇重試或不重試,嗯,自行車保住了。

    CAS算法將內存位置的內容與給定值進行比較,只有當它們相同時,纔將該內存位置的內容修改爲給定的新值。這是作爲單個原子操作完成的。原子性保證了新值的計算是基於最新的信息;如果該值已由另一個線程同時更新,則寫入將失敗。操作結果必須表明是否執行了替換;這可以通過一個簡單的布爾響應(這種變體通常稱爲比較和設置)來實現,也可以通過返回從內存位置讀取的值(而不是寫入其中的值)來實現。(我承認這段是抄的)。其實CAS是依賴於現在CPU的指令操作。

    CAS的原子操作類包括:

    更新基本數據類型在java.util.concurrent.atomic包下以Atomic開頭的類。

    下面只寫幾個示例代碼

1.2. 更新基本類型:AtomicInteger

1.2.1. 主要方法

    getAndIncrement():獲取值後加一

    getAndDecrement():獲取值後減一

    incrementAndGet():加一後獲取值

    decrementAndGet():減一後獲取值

    get():獲取值

    compareAndSet(int expect, int update):設置期望值之前比對更新值是否與內存中的值相同,若不相同返回false,若相同設置值並返回true

1.2.2. 上代碼

package com.concurrent.coline.part7;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 類說明:AtomicInt
 */
public class UseAtomicInt {

    static AtomicInteger ai = new AtomicInteger(10);

    public static void main(String[] args) {
        //10--->11
        System.out.println(ai.getAndIncrement());
        //11-->10
        System.out.println(ai.getAndDecrement());
        //10--->11
        System.out.println(ai.incrementAndGet());
        //11-->10
        System.out.println(ai.decrementAndGet());
        System.out.println(ai.get());
        System.out.println(ai.compareAndSet(11, 10));
        System.out.println(ai.compareAndSet(10, 10));
    }
}

運行結果:

   代碼位置:compare-swap模塊part7

1.3. 更新引用類型:AtomicReference

1.3.1. 主要方法

    compareAndSet(int expect, int update):設置期望值之前比對更新值是否與內存中的值相同,若不相同返回false,若相同設置值 並返回true

1.3.2. 上代碼

package com.concurrent.coline.part7;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 類說明:演示引用類型的原子操作類
 */
public class UseAtomicReference {

    private static AtomicReference<UserInfo> userRef = new AtomicReference<UserInfo>();

    public static void main(String[] args) {
        //要修改的實體的實例
        UserInfo user = new UserInfo("Tom", 15);
        userRef.set(user);

        //要變化的新實例
        UserInfo updateUser = new UserInfo("Coline", 17);
        System.out.println(userRef.compareAndSet(user, updateUser));

        System.out.println(userRef.get().getName());
        System.out.println(userRef.get().getAge());
        System.out.println(user.getName());
        System.out.println(user.getAge());
    }

    //定義一個實體類
    private static class UserInfo {
        private String name;
        private int age;

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

        private String getName() {
            return name;
        }

        private int getAge() {
            return age;
        }
    }

}

運行結果:

    可以看到這裏的更新並不會修改user的實例

    代碼位置:compare-swap模塊part7

1.4. 帶版本戳更新:AtomicStampedReference

1.4.1. 說明

    這個類是爲了解決ABA問題而存在的,比如alpha線程想把A->B,但是beta線程做了A->B->A,在alpha線程來做操作的時候無法判斷A被beta線程變過,也許我們就存在這樣的業務場景,不允許alpha再進行業務修改,而是提醒我們(我想到的是類似於祕鑰修改這種安全相關的場景),這時候將修改對象與版本相關聯就成爲了一種很好的解決方案。

1.4.2. 上代碼

package com.concurrent.coline.part7;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 類說明:演示帶版本戳的原子操作類
 */
public class UseAtomicStampedReference {

    private static AtomicStampedReference<String> asr =
            new AtomicStampedReference<>("Coline", 0);


    public static void main(String[] args) throws InterruptedException {
        //獲取初始的版本號
        final int oldStamp = asr.getStamp();
        final String oldReferenc = asr.getReference();

        Thread rightStampThread = new Thread(() -> {

            System.out.println(Thread.currentThread().getName()
                    + "當前變量值:" + oldReferenc + "計劃修改時版本戳:" + oldStamp + "-"
                    + asr.compareAndSet(oldReferenc, oldReferenc + "Java",
                    oldStamp, oldStamp + 1));


        });

        Thread errorStampThread = new Thread(() -> {

            System.out.println(Thread.currentThread().getName()
                    + "當前變量值:" + oldReferenc + "計劃修改時版本戳:" + oldStamp + "-"
                    + asr.compareAndSet(oldReferenc, oldReferenc + "Java",
                    oldStamp, oldStamp + 1));


        });

        rightStampThread.start();
        rightStampThread.join();
        errorStampThread.start();
        errorStampThread.join();
        System.out.println(asr.getReference() + "===========" + asr.getStamp());

    }
}

運行結果:

代碼位置:compare-swap模塊part7

1.5. 修改數組:AtomicIntegerArray

    比較簡單,直接上代碼:

package com.concurrent.coline.part7;

import java.util.concurrent.atomic.AtomicIntegerArray;


/**
 * 類說明:AtomicArray
 */
public class AtomicArray {
    static int[] value = new int[]{1, 2};
    
    static AtomicIntegerArray ai = new AtomicIntegerArray(value);

    public static void main(String[] args) {
        ai.getAndSet(0, 3);
        System.out.println(ai.get(0));
        System.out.println(value[0]);

    }
}

運行結果:

代碼位置:compare-swap模塊part7

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