在說AtomicStampedReference前,我們回顧一下CAS是什麼?CAS 是Compare-And-Swap的簡寫即對比交換,它在保證數據原子性的前提下儘可能的減少了鎖的使用 也就是我們經常是的樂觀鎖 ;目標值 期望值。
Java中實現CAS主要還是依賴unsafe類提供的底層方法,看源碼我們可知其都是native 方法;所以說是原子性操作
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);
CAS是如何導致ABA的尼,什麼是ABA?
線程1準備用CAS修改變量值A,在此之前,其它線程將變量的值由A替換爲B,又由B替換爲A,然後線程1執行CAS時發現變量的值仍然爲A,所以CAS成功。但實際上這時的現場已經和最初不同了。
public static AtomicInteger a = new AtomicInteger(1);
public static void main(String[] args){
Thread main = new Thread(() -> {
System.out.println("操作線程" + Thread.currentThread() +",初始值 = " + a); //定義變量 a = 1
try {
Thread.sleep(1000); //等待1秒 ,以便讓干擾線程執行
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作
System.out.println("操作線程" + Thread.currentThread() +",CAS操作結果: " + isCASSuccess);
},"主操作線程");
Thread other = new Thread(() -> {
Thread.yield(); //確保thread-main線程優先執行
a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2
System.out.println("操作線程" + Thread.currentThread() +",【increment】 ,值 = "+ a);
a.decrementAndGet(); // a 減 1, a - 1 = 2 - 1 = 1
System.out.println("操作線程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);
},"干擾線程");
main.start();
other.start();
}
// 輸出
> 操作線程Thread[主操作線程,5,main],初始值 = 1
> 操作線程Thread[干擾線程,5,main],【increment】 ,值 = 2
> 操作線程Thread[干擾線程,5,main],【decrement】 ,值 = 1
> 操作線程Thread[主操作線程,5,main],CAS操作結果: true
爲了解決Atomic可能出現的ABA問題 ,jdk提供了AtomicStampedReference類,很簡單 AtomicStampedReference 除了比較對象本身的 期望值,目標值之外,還增加了一個時間戳版本號,用於記錄對象的版本;對對象修改時,會同時比對對象和版本號。
下面是截取的源碼 其封裝了一個內部類
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
public boolean weakCompareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
return compareAndSet(expectedReference, newReference,
expectedStamp, newStamp);
}
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
casPair(current, Pair.of(newReference, newStamp)));方法就是直接調用unsafe的交換方法;和上面cas java實現一樣。