目錄頁: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