1、什麼是CAS?java
比較並交換(compare and swap),它是一條CPU併發原語。判斷內存某個位置的值是否爲預期值,若是是的話則更新爲新值,這個過程是原子的。多線程
原語是操做系統的用語,是由若干條指定組成,用於完成某個功能的一個過程,而且原語的執行必須是連續的,在執行過程當中不容許被中斷,也就是說CAS是一條CPU的原子指令,不會形成所謂的數據不一致問題。併發
public class Solution { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5); System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(5,33)+ ". current data is: " + atomicInteger.get()); } }
運行結果:this
true. current data is: 22 false. current data is: 22
public class Solution { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5); System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(22,33)+ ". current data is: " + atomicInteger.get()); } }
運行結果:atom
true. current data is: 22 true. current data is: 33
結果說明:spa
一、第一次運行,因爲在物理內存中的值是5,因此符合expected的值,就修改成了22,而第二條語句的expected爲5,可是如今值已經爲22,因此不符合預期,就不能改變值。操作系統
二、第二次運行,每次的expected的值都爲物理內存中的值,因此都進行了修改。線程
2、CAS底層原理,什麼是UnSafe?指針
要點:UnSafe類(存在rt.jar中)+CAS自旋鎖code
AtomicInteger的源碼
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; }
一、Unsafe類
這是CAS的核心類,因爲Java方法沒法直接訪問底層系統,須要經過本地(native)方法來訪問,UnSafe至關於一個後門,基於該類能夠直接操做特定內存的數據
UnSafe類存在於sun.misc包中,其內部方法操做能夠像C的指針同樣直接操做內存,所以Java中CAS操做執行依賴於UnSafe類
UnSafe類中的全部方法都是native修飾的,也就是說UnSafe類中的方法都直接調用操做系統底層資源執行相應任務
二、變量valueOffset,表示該變量在內存中的偏移地址,由於UnSafe就是根據內存偏移地址獲取數據的。
//this:當前對象 //valueOffset:內存偏移量 public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } //var1 AtomicInteger對象自己 //var2 該對象值的引用地址 //var4 須要變更的值 //var5 用var1 var2找出主內存中真實的值 //用該對象當前值與var5比較,若是相同,更新var5+var4返回true,若是不一樣,繼續取值比較,直到更新完成 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; }
總結:getAndIncrement()底層調用unsafe類方法,傳入三個參數,unsafe.getAndAddInt()底層使用CAS思想,若是比較成功加1,若是比較失敗則從新得到,再比較,直到成功
三、變量value用volatile修飾,保證了多線程之間的內存可見性
2、CAS缺點?
一、循環時間開銷大(若是CAS失敗,會一直嘗試)
2、只能保證一個共享變量的原子操做。(對多個共享變量操做時,循環CAS沒法保證操做的原子性,只能用加鎖來保證)
三、存在ABA問題
4、原子類AtomicInteger類ABA問題及解決方案
一、ABA問題是怎麼產生的?
當第一個線程執行CAS(V,E,U)操做,在獲取到當前變量V,準備修改成新的值U以前。另外兩個線程已經連續修改了兩次變量V的值,使得該值又恢復爲舊的值,這樣咱們就沒法正確判斷這個變量是否已經被修改過
二、ABA問題的解決方案:
AtomicStampedReference:是一個帶有時間戳的對象引用,在每次修改後,不只會設置新的值,還會記錄修改的時間
AtomicMarkableReference:維護的是一個Boolean值的標識,這種方式並不能徹底防止ABA問題的發生,只能減小ABA發生的機率
public class Solution { static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1); public static void main(String[] args) throws InterruptedException { 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) + " " + atomicReference.get()); }, "t2").start(); TimeUnit.SECONDS.sleep(2); System.out.println("========ABA問題的解決========="); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "線程第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() + "線程第2次版本號:" + atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "線程第3次版本號:" + atomicStampedReference.getStamp()); }, "t3").start(); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "線程第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() + "修改爲功否:" + result + " 當前最新版本號:" + atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName() + "當前最新值:" + atomicStampedReference.getReference()); }, "t4").start(); } }
運行結果:
========ABA問題的產生========= true 2019 ========ABA問題的解決========= t3線程第1次版本號:1 t4線程第1次版本號:1 t3線程第2次版本號:2 t3線程第3次版本號:3 t4修改爲功否:false 當前最新版本號:3 t4當前最新值:100
5、原子更新引用
class User { User(String name, int age) { this.name = name; this.age = age; } private String name; private int age; } public class Solution { static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1); public static void main(String[] args) throws InterruptedException { AtomicReference<User> atomicReference = new AtomicReference<>(); User user = new User("monster", 18); User updateUser = new User("jack", 25); atomicReference.set(user); System.out.println(atomicReference.get()); atomicReference.compareAndSet(user, updateUser); System.out.println(atomicReference.get()); } }
運行結果:
User@74a14482
User@1540e19d