2.原子變量 CAS算法
前面提到,使用volatile無法保證 變量狀態的原子性操作,所謂原子性,就是不可再分
如:i++的原子性問題,i++ 的操作實際上分為三個步驟 "讀-改-寫"
(1)保存i的值(一個臨時變量中)
(2)遞增i
(3)返回已保存的值
當在並發的條件下執行 i++,
線程1執行 i++,先從主存中 獲取 i 的 值(假設初值i=0),還未等 執行i = i + 1,此時線程2進來,也從主存中獲取 i 的 值(0)
然後 線程1 執行了 i = i + 1;(i=1) 線程2再執行 i = i + 1(i=1),這種結果是錯誤的
即使使用 volatile ,保證內存的可見性,也是不管用的,即使在主存中進行修改操作,一樣會產生這種錯誤
此時,可以采用 CAS算法 ,CAS算法 是 樂觀鎖的一種(沖突檢測)(hibernate 的樂觀鎖是 加一個 version 字段,來判斷是否發生了並發)
CAS(Compare-And-Swap) 算法 保證數據變量的原子性
CAS 算法是硬件對於並發操作的支持
CAS 包含了三個操作數:
* ①內存值 V
* ②預估值 A
* ③更新值 B
* 當且僅當 V == A 時, V = B; 否則,不會執行任何操作。
過程分析:同樣在 並發條件下 執行 i++
1、線程1 執行 i++,先從主存中獲取 i 的值(V=i=0) (設i=0),此時線程2進來,也從主存中 獲取 i 的 值(0)
2、然後線程1 執行 getAndIncrement,即比較和替換一起執行,(過程:再從內存中讀取一遍i的值(A=i=0),讓 A(0) 與 V(0)進行比較,
發現 V==A,此時,B = i+1,將B的值更新到內存中(V = B) )
3、然後 線程2 開始執行 getAndIncrement,即比較和替換一起執行,過程 和 上述類似,不過再從內存讀一次值,i的值已經變成了 1 ,即A的值也為1
讓A(1)與V(0)進行比較比較,發現 V!=A, 不執行任何操作
註:將 V 與 A 比較的意義在於 判斷 要更新的值(V)是否發生了改變,如果沒有發生改變,則進行 V 的 更新,否則不做任何操作
再發現 V!=A 後,與 synchronize 不一樣的 是,這裏不會發生阻塞,不會等當前線程執行完後,再由CPU 分配時間去給線程2去執行,
而是不停的 循環發送請求,緊接著再去嘗試,再去更新,這也是 CAS算法 比普通同步鎖的做法 效率要高的原因
采用CAS算法之後,當有多個線程訪問 內存中的共享資源,一次只會有一個線程成功,其他線程都會失敗
java.util.concurrent.atomic 包下提供了一些原子操作的常用類:裏面頻繁的使用到了CAS算法來保證變量狀態的原子性操作
? AtomicBoolean 、 AtomicInteger 、 AtomicLong 、 AtomicReference
? AtomicIntegerArray 、 AtomicLongArray
? AtomicMarkableReference
? AtomicReferenceArray
? AtomicStampedReference
l核心方法:boolean compareAndSet(expectedValue, updateValue) (也是CAS裏面的核心,即比較和替換一起執行)
1 /* 2 * 一、i++ 的原子性問題:i++ 的操作實際上分為三個步驟“讀-改-寫” 3 * int i = 10; 4 * i = i++; //10 5 * 6 * (1)保存i的值(一個臨時變量中) 7 (2)遞增i 8 (3)返回已保存的值 9 * 10 * 二、原子變量:在 java.util.concurrent.atomic 包下提供了一些原子變量。 11 * 1. volatile 保證內存可見性 12 * 2. CAS(Compare-And-Swap) 算法保證數據變量的原子性 13 * CAS 算法是硬件對於並發操作的支持 14 * CAS 包含了三個操作數: 15 * ①內存值 V 16 * ②預估值 A 17 * ③更新值 B 18 * 當且僅當 V == A 時, V = B; 否則,不會執行任何操作。 19 */ 20 21 public class TestAtomic { 22 public static void main(String[] args) { 23 24 AtomicDemo ad = new AtomicDemo(); 25 for(int i = 0;i<10;i++) { 26 new Thread(ad).start(); 27 } 28 } 29 } 30 31 class AtomicDemo implements Runnable { 32 //創建原子變量 33 private AtomicInteger i = new AtomicInteger(0); 34 @Override 35 public void run() { 36 try { 37 Thread.sleep(200); 38 } catch (InterruptedException e) { 39 40 } 41 //查看源碼可以發現 循環一直調用compareAndSet(ExceptionValue,UpdateValue)方法,即比較和替換一起執行, 42 //並且如果失敗了,會不停的去嘗試更新(裏面使用到CAS算法的) 43 System.out.println(i.getAndIncrement()); 44 } 45 46 47 }
2.原子變量 CAS算法