點贊再看,養成習慣,微信搜索「小大白日志」關注這個搬磚人。
文章不定期同步公眾號,還有各種一線大廠面試原題、我的學習系列筆記。
CAS如何解決ABA問題
什么是ABA:在CAS過程中,線程1、線程2分別從內存中拿到了當前值為A,同時線程2把當前值A改為B,隨后又把B改回來變為A,此后線程1檢查到當前值仍為A而導致執行cas成功,但這個過程卻發生了ABA問題,現場資源可能和當初不一樣了(線程2把當前值由A->B->A)
解決方法:版本號機制,利用版本號標記線程1拿到的‘當前值’的版本,若線程2進行了A->B->A操作,則版本號會改變,那線程1再次拿到的‘當前值’的版本和第一次的肯定是不同的,從而判定cas失敗;
java代碼中AtomicStampedReference類的cas方法實現了版本號機制,可用它來解決ABA問題:
/**
* @Description 測試解決CAS中的ABA問題
* @Author afei
* @date:2021/6/27
*/
public class AtomicStampedReferenceTest {
//AtomicInteger和AtomicStampedReference的初始值都為100,但AtomicStampedReference帶了版本號0
private static AtomicInteger atomicInt = new AtomicInteger(100);
private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);
public static void main(String[] args) {
//用AtomicInteger會產生ABA問題
aba1();
//用AtomicStampedReference解決cas過程中的ABA問題
aba2();
}
public static void aba1(){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
boolean c3 = atomicInt.compareAndSet(100, 101);
System.out.println(c3); // true
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//t2改變100->101->100,compareAndSet的參數:期待的值,新值
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
}
});
t1.start();
t2.start();
try {
//t1,t2執行完,主線程才能繼續執行
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void aba2(){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
int stamp = atomicStampedRef.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
//compareAndSet中四個參數分別為:期待的值,新值,期待的版本號,新的版本號
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(c3); // false
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
//t2改變100->101->100,compareAndSet中四個參數分別為:期待的值,新值,期待的版本號,新的版本號
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
}
});
t1.start();
t2.start();
}
}
AtomicInteger的原理
Java可以利用Syschronized、ReentrantLock、AtomicInteger類實現線程安全,AtomicInteger封裝了一個【volatile int value】屬性,它可以對這個屬性進行許多原子性操作,這些原子性操作大多是基於cas原理,而在cas中,AtomicInteger使用的是一個叫Unsafe的類中的方法,Unsafe可以提供一些底層操作,也就是CPU特定的指令集,進而避免了並發問題(Unsafe是一個很危險的類,它可以做一些和內存相關的操作)
OK,如果文章哪里有錯誤或不足,歡迎各位留言。
創作不易,各位的「三連」是二少創作的最大動力!我們下期見!