CAS / ABA
標籤(空格分隔): 操作系統
1. CAS 解決 Volatile 不保證原子性的問題
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
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;
}
Volatile不保證原子性的解決方案, 在其中我們可以看到我們通過
atomicInteger.getAndIncrement()
, 獲取值並且將其+1, 重點在於this.compareAndSwapInt(var1, var2, var5, var5 + var4)
通過樂觀鎖的方法, 相當於實現了原子性的操作.
2. CAS 導致的 ABA 問題解決
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* CAS會導致ABA問題:
* CAS算法實現一個重要前提需要取出內存中某時刻的數據並且在當下時刻進行比較並替換(Compare And Set), 那麼在這個時間差的時候可能會導致數據的變化.
* 比如: 一個線程one從內存位置中V中取出A, 這個時候另一個線程two也從內存中取出A. 此時線程one和two都持有位置V中A的備份, 在這個時候two將A->B->A 並寫入主存當中.
* 線程one去進行CAS操作的時候, 發現內存中仍然是A, 然後線程one操作成功.
*
* 這個問題主要看線程two對中間操作對 線程one有影響沒, 如果有影響的話. 需要解決一下CAS問題.
*/
public class Test {
private static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
/* ABA問題產生實例 Start */
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)+"\t"+atomicReference.get());
}).start();
/* ABA問題產生實例 End */
/* ABA問題解決實例 Start */
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第一次版本號 : " + atomicStampedReference.getStamp());
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() + "\t 第二次版本號 : " + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t 第三次版本號 : " + atomicStampedReference.getStamp());
}, "t3").start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第一次版本號 : " + atomicStampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("是否修改成功: " + atomicStampedReference.compareAndSet(100, 2019, 1, atomicStampedReference.getStamp() + 1) + "\t 當前的stamp值: " + atomicStampedReference.getStamp());
System.out.println("當前最新值: " + atomicStampedReference.getReference());
}, "t4").start();
/* ABA問題 解決實例 End */
}
}