一、CAS概念與原理 CAS,全稱Compare And Swap(比較與交換),解決多線程並行情況下使用鎖造成性能損耗的一種機制。 實現思想 CAS(V, A, B),V為記憶體地址、A為預期原值,B為新值。如果記憶體地址的值與預期原值相匹配,那麼將該位置值更新為新值。否則,說明已經被其他線程更新,處 ...
一、CAS概念與原理
CAS,全稱Compare And Swap(比較與交換),解決多線程並行情況下使用鎖造成性能損耗的一種機制。
實現思想 CAS(V, A, B),V為記憶體地址、A為預期原值,B為新值。如果記憶體地址的值與預期原值相匹配,那麼將該位置值更新為新值。否則,說明已經被其他線程更新,處理器不做任何操作;無論哪種情況,它都會在 CAS 指令之前返回該位置的值。而我們可以使用自旋鎖,迴圈CAS,重新讀取該變數再嘗試再次修改該變數,也可以放棄操作。
CAS操作由處理器提供支持,是一種原語。原語是操作系統或電腦網路用語範疇。是由若幹條指令組成的,用於完成一定功能的一個過程,具有不可分割性,即原語的執行必須是連續的,在執行過程中不允許被中斷。如 Intel 處理器,比較並交換通過指令的 cmpxchg 系列實現。處理器相關指令不做過多介紹,有興趣的可自行查閱資料。
二、JDK1.8 中的CAS
Unsafe類,在sun.misc包下,不屬於Java標準。Unsafe類提供一系列增加Java語言能力的操作,如記憶體管理、操作類/對象/變數、多線程同步等。其中與CAS相關的方法有以下幾個:
//var1為CAS操作的對象,offset為var1某個屬性的地址偏移值,expected為期望值,var2為要設置的值,利用JNI來完成CPU指令的操作 public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2); public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2); public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);
/** 如果CAS成功,return oldValue, oldValue = oldValue + addValue * 如果CAS失敗,自旋,一直運行,直到成功為止 */ public final Xxx getAndAddXxx(Object var1, long offset, long addValue) { int oldValue; do { oldValue = this.getIntVolatile(var1, offset); } while(!this.compareAndSwapInt(var1, offset, oldValue, oldValue + addValue)); return oldValue; } /** 如果CAS成功,return oldValue, oldValue = newValue * 如果CAS失敗,自旋,一直運行,直到成功為止 */ public final Xxx getAndSetXxx(Object var1, long offset, Object newValue) { int oldValue; do { oldValue = this.getXxxVolatile(var1, offset); } while(!this.compareAndSwapXxx(var1, offset, oldValue, newValue)); return oldValue; }
一般不建議使用Unsafe類,除非對它有很深入的瞭解。
java.util.concurrent包中大量使用了CAS原理,如AtomicInteger類,都是調用上面幾個Unsafe方法保證多線程數據的正確性
以下是AtomicInteger的CAS操作相關源碼
1 public class AtomicInteger extends Number implements java.io.Serializable { 2 private static final long serialVersionUID = 6214790243416807050L; 3 4 // setup to use Unsafe.compareAndSwapInt for updates 5 // Unsafe類,提供一系列增強Java的功能,如記憶體管理、操作類/對象/變數、多線程同步等。不建議開發者調用 6 private static final Unsafe unsafe = Unsafe.getUnsafe(); 7 // 獲取對象某個屬性的地址偏移值 8 private static final long valueOffset; 9 10 static { 11 try { 12 // value相對“起始地址”的偏移量 13 valueOffset = unsafe.objectFieldOffset 14 (AtomicInteger.class.getDeclaredField("value")); 15 } catch (Exception ex) { throw new Error(ex); } 16 } 17 18 // value值, volatile修飾,保證不同線程間的可見性 19 private volatile int value; 20 public AtomicInteger(int initialValue) { value = initialValue; } 21 public AtomicInteger() {} 22 23 public final int get() { return value; } 24 public final void set(int newValue) { value = newValue; } 25 26 /** 27 * Eventually sets to the given value. 28 * 29 * @param newValue the new value 30 * @since 1.6 31 */ 32 public final void lazySet(int newValue) { 33 //有序或者有延遲的putIntVolatile方法 34 unsafe.putOrderedInt(this, valueOffset, newValue); 35 } 36 37 /** 38 * Atomically sets to the given value and returns the old value. 39 * @param newValue the new value 40 * @return the previous value 41 */ 42 public final int getAndSet(int newValue) { 43 return unsafe.getAndSetInt(this, valueOffset, newValue); 44 } 45 46 /** 47 * Atomically sets the value to the given updated value 48 * if the current value {@code ==} the expected value. 49 * @param expect the expected value 50 * @param update the new value 51 * @return {@code true} if successful. False return indicates that 52 * the actual value was not equal to the expected value. 53 */ 54 public final boolean compareAndSet(int expect, int update) { 55 // JNI調用,實現CAS 56 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 57 } 58 59 /** 60 * i++ 操作 61 * Atomically increments by one the current value. 62 * @return the previous value 63 */ 64 public final int getAndIncrement() { 65 return unsafe.getAndAddInt(this, valueOffset, 1); 66 } 67 68 /** 69 * i-- 操作 70 * Atomically decrements by one the current value. 71 * @return the previous value 72 */ 73 public final int getAndDecrement() { 74 return unsafe.getAndAddInt(this, valueOffset, -1); 75 } 76 77 /** 78 * return i, i = i + n 操作 79 * Atomically adds the given value to the current value. 80 * @param delta the value to add 81 * @return the previous value 82 */ 83 public final int getAndAdd(int delta) { 84 return unsafe.getAndAddInt(this, valueOffset, delta); 85 } 86 87 /** 88 * ++i 操作 89 * Atomically increments by one the current value. 90 * @return the updated value 91 */ 92 public final int incrementAndGet() { 93 return unsafe.getAndAddInt(this, valueOffset, 1) + 1; 94 } 95 96 /** 97 * --i 操作 98 * Atomically decrements by one the current value. 99 * @return the updated value 100 */ 101 public final int decrementAndGet() { 102 return unsafe.getAndAddInt(this, valueOffset, -1) - 1; 103 } 104 105 /** 106 * i = i + n ,return i操作 107 * Atomically adds the given value to the current value. 108 * @param delta the value to add 109 * @return the updated value 110 */ 111 public final int addAndGet(int delta) { 112 return unsafe.getAndAddInt(this, valueOffset, delta) + delta; 113 } 114 // 其餘函數,略... 115
三、CAS缺點
CAS有幾個缺點:
1、ABA問題。當第一個線程執行CAS操作,尚未修改為新值之前,記憶體中的值已經被其他線程連續修改了兩次,使得變數值經歷 A -> B -> A的過程。
解決方案:添加版本號作為標識,每次修改變數值時,對應增加版本號; 做CAS操作前需要校驗版本號。JDK1.5之後,新增AtomicStampedReference類來處理這種情況。
2、迴圈時間長開銷大。如果有很多個線程併發,CAS自旋可能會長時間不成功,會增大CPU的執行開銷。
3、只能對一個變數進原子操作。JDK1.5之後,新增AtomicReference類來處理這種情況,可以將多個變數放到一個對象中。