CAS即對比交換,它在保證數據原子性的前提下儘量的減小了鎖的使用,不少編程語言或者系統實現上都大量的使用了CAS。編程
JAVA中的cas主要使用的是Unsafe方法,Unsafe的CAS操做主要是基於硬件平臺的彙編指令,目前的處理器基本都支持CAS,只不過不一樣的廠家的實現不同罷了。app
Unsafe提供了三個方法用於CAS操做,分別是編程語言
public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update); public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update); public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);
Unsafe.objectFieldOffset(Field valueField)
獲取)具體過程爲每次在執行CAS操做時,線程會根據valueOffset去內存中獲取當前值去跟expect的值作對好比果一致則修改並返回true,若是不一致說明有別的線程也在修改此對象的值,則返回falseoop
Unsafe類中compareAndSwapInt的具體實現:this
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) UnsafeWrapper("Unsafe_CompareAndSwapInt"); oop p = JNIHandles::resolve(obj); jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); return (jint)(Atomic::cmpxchg(x, addr, e)) == e; UNSAFE_END
線程1準備用CAS修改變量值A,在此以前,其它線程將變量的值由A替換爲B,又由B替換爲A,而後線程1執行CAS時發現變量的值仍然爲A,因此CAS成功。但實際上這時的現場已經和最初不一樣了。atom
aba.pngspa
public static AtomicInteger a = new AtomicInteger(1); public static void main(String[] args){ Thread main = new Thread(() -> { System.out.println("操做線程" + Thread.currentThread() +",初始值 = " + a); //定義變量 a = 1 try { Thread.sleep(1000); //等待1秒 ,以便讓干擾線程執行 } catch (InterruptedException e) { e.printStackTrace(); } boolean isCASSuccess = a.compareAndSet(1,2); // CAS操做 System.out.println("操做線程" + Thread.currentThread() +",CAS操做結果: " + isCASSuccess); },"主操做線程"); Thread other = new Thread(() -> { a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2 System.out.println("操做線程" + Thread.currentThread() +",【increment】 ,值 = "+ a); a.decrementAndGet(); // a 減 1, a - 1 = 2 - 1 = 1 System.out.println("操做線程" + Thread.currentThread() +",【decrement】 ,值 = "+ a); },"干擾線程"); main.start(); other.start(); }
// 輸出 > 操做線程Thread[主操做線程,5,main],初始值 = 1 > 操做線程Thread[干擾線程,5,main],【increment】 ,值 = 2 > 操做線程Thread[干擾線程,5,main],【decrement】 ,值 = 1 > 操做線程Thread[主操做線程,5,main],CAS操做結果: true
解決ABA最簡單的方案就是給值加一個修改版本號,每次值變化,都會修改它版本號,CAS操做時都對比此版本號。線程
aba_2.pngcode
AtomicStampedReference主要維護包含一個對象引用以及一個能夠自動更新的整數"stamp"的pair對象來解決ABA問題。對象
//關鍵代碼 public class AtomicStampedReference<V> { private static class Pair<T> { final T reference; //維護對象引用 final int stamp; //用於標誌版本 private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); } } private volatile Pair<V> pair; .... /** * expectedReference :更新以前的原始值 * newReference : 將要更新的新值 * expectedStamp : 期待更新的標誌版本 * newStamp : 將要更新的標誌版本 */ public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; //獲取當前pair return expectedReference == current.reference && //原始值等於當前pair的值引用,說明值未變化 expectedStamp == current.stamp && // 原始標記版本等於當前pair的標記版本,說明標記未變化 ((newReference == current.reference && newStamp == current.stamp) || // 將要更新的值和標記都沒有變化 casPair(current, Pair.of(newReference, newStamp))); // cas 更新pair } }
private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(1, 0); public static void main(String[] args){ Thread main = new Thread(() -> { System.out.println("操做線程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference()); int stamp = atomicStampedRef.getStamp(); //獲取當前標識別 try { Thread.sleep(1000); //等待1秒 ,以便讓干擾線程執行 } catch (InterruptedException e) { e.printStackTrace(); } boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1); //此時expectedReference未發生改變,可是stamp已經被修改了,因此CAS失敗 System.out.println("操做線程" + Thread.currentThread() +",CAS操做結果: " + isCASSuccess); },"主操做線程"); Thread other = new Thread(() -> { atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1); System.out.println("操做線程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference()); atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1); System.out.println("操做線程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference()); },"干擾線程"); main.start(); other.start(); }
// 輸出 > 操做線程Thread[干擾線程,5,main],【increment】 ,值 = 2 > 操做線程Thread[干擾線程,5,main],【decrement】 ,值 = 1 > 操做線程Thread[主操做線程,5,main],初始值 a = 1 > 操做線程Thread[主操做線程,5,main],CAS操做結果: true