1.volatile是java虛擬機提供的輕量級的同步機制,三個特點①保證可見性(JMM模型)②不保證原子性(存在往主內存寫數據時覆蓋)③禁止指令重排。
2.如何解決原則性:①加sync鎖②使用JUC包下AtomicInteger類(CAS原理)
3.CAS(compareAndSwap)底層原理unsafe類:比較當前工作內存中的值和主內存中的值,如果相同則指定規定操作,否則繼續比較直到主內存和工作內存中的值一致性。
4.CAS有3個操作數,內存值V,舊的預期值A,要修改的更新值B,當且僅當預期值A和內存值V相同時,將內存值修改爲B否則什麼也不做。CAS缺點①循環時間長開銷大,②只能保證一個共享變量的原子操作③引出ABA問題。
5.CAS會導致ABA問題:
CAS算法實現一個重要前提時需要去除內存中某時刻的數據並在當下時刻比較並替換,那麼在這個時間差裏會導致數據變化。
比如說一個線程one從內存位置V取出A,這時候另一個線程two也從內存中取出A,並且線程two進行了一些操作將值變成了B,然後線程two又將V位置的數據變成A,這時候線程one進行CAS操作發現內存中仍然是A,然後線程one操作成功,儘管線程One的CAS操作成功,但是並不代表這個過程就是沒有問題的。
/**
* 描述: CAS導致的ABA問題的解決 AtomicStampReference類
*
* @author [email protected]
* @create 2020/2/21 11:55
* @since 2.16.3
*/
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
// 解決ABA問題的類AtomicStampedReference類(初始值,初始版本號)
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(()-> {
// 暫停1秒鐘t2線程,保證上面的t1線程完成了一次ABA操作
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100,2019)+"\t"+atomicReference.get());
}, "t2").start();
System.out.println("----------------以下是ABA問題的解決-------------");
new Thread(()-> {
// 版本號
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次版本號"+stamp);
// 暫停1秒鐘t3線程
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第一次版本號"+stamp);
// 暫停3秒鐘t4線程,保證上面的t3線程完成了一次ABA操作
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();
}
}