CAS是什么?
比較並交換
例子1:
public class ABADemo1 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5,2019)+"\t當前值是:"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5,1024)+"\t當前值是:"+atomicInteger.get());
}
}
結果如下:
CAS底層原理?
1.自旋鎖
2.UnSafe類
atomicInteger.getAndIncrement();

底層調用的其實是unsafe方法,關系見下圖:
va1 AtomicInteger對象本身。
var2 該對象的引用地址。
var4 需要變動的數量。
var5 是用var1 var2找出的主內存中真實的值,用該對象當前的值與var5比較,如果相同,更新var5+var4並且返回true,如果不同,繼續取值然后再比較,直到更新完成。(自旋)
最后通過native調用本地方法,該方法的實現位於unsafe.cpp中。
1.Unsafe類:
是CAS的核心類,由於Java方法無法直接訪問底層系統,需要通過本地方法(native)來訪問,Unsafe相當於一個后門,基於該類可以直接操作特定內存的數據。Unsafe類存在於sun.misc包中,其內部方法操作可以像C的指針一樣直接操作內存,因為java 中CAS的操作執行依賴於Unsafe類的方法。
注意Unsafe類中的所有方法都是native修飾的,也就是說Unsafe類中的方法都直接調用操作系統底層資源執行相應任務
2.變量valueOffset,表示該變量值在內存中的偏移地址,因為Unsafe就是根據內存偏移地址來獲取數據的。
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
3.變量vaue用volatile修飾,保證了多線程之間的內存可見性。
CAS 的全稱為Compare-And-Swap,它是一條CPU並發原語。
它的功能是判斷內存某個位置的值是否為預期值,這個過程是原子的
CAS並發原語體現在Java語言就是sun.misc.Unsafe類中的各個方法。調用UnSafe類中的CAS方法,JVM會幫我們實現CAS匯編指令。這是一種完全依賴於硬件的功能。通過它實現了原子操作。再次強調,由於CAS是一種系統原語,原語屬於操作系統用於范疇,是由若干條指令組成的,用於完成某個功能的一個過程,並且原語的執行必須是連續的,再執行過程中不允許被中斷,也就是說CAS是一條CPU的原子指令,不會造成所謂的數據不一致問題。
CAS 的缺點
1.循環時間長開銷很大。
2.只能保證一個共享變量的原子操作。
3.會有ABA問題。
CAS會導致"ABA問題"
CAS算法實現一個重要前提需要取出內存中某時刻的數據並再當下時刻比較並替換,那么再這個時間差會導致數據的變化。
比如說一個線程one從內存位置V中取出A,這時候另一個線程two也從內存中取出A,並且線程two進行了一些操作將值變成了B,然后線程two又將V位置的數據變成A, 這時候線程one進行CAS操作發現內存中仍然是A,然后線程one操作成功。盡管線程one的CAS操作成功,但不代表這個操作過程就是沒有問題的。
AtomicReference原子引用例子:
@Getter
@ToString
@AllArgsConstructor
class User {
String userName;
int age;
}
/**
* AtomicReference原子引用,想對某個類進行包裝,可以參考這個類的寫法
*/
public class AtomicReferenceDemo {
public static void main(String[] args) {
User z3 = new User("z3",22);
User li4 = new User("li4",25);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
}
}
運行結果見下圖:
解決ABA問題 加版本號例子:
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("-------------存在ABA問題");
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());
},"t2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------以下是解決ABA問題");
new Thread(() ->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本號"+stamp);
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第2次版本號"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第3次版本號"+atomicStampedReference.getStamp());
},"t3").start();
new Thread(() ->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本號"+stamp);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);
System.out.println(Thread.currentThread().getName()+"\t修改成功否"+result+"\t 當前版本號"+atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"\t當前實際最新值"+atomicStampedReference.getReference());
},"t4").start();
}
}
運行結果見下圖: