簡述java中cas

南小瓜發表於2018-12-17

cas原理

cas是compareAndSwap的縮寫,可以看出就是比較比替換的意思。cas中有三個值,記憶體值V,舊的預期值E,更新值U,當且僅當V==E時,才進行更新,否則返回V。

cas應用

以java.util.concurrent包中的AtomicInteger為例。先演示程式碼:

public class AtomicIntegerTest {

	private static AtomicInteger atomicInteger=new AtomicInteger(5);//設定初始值

	public static void main(String[] args) {
		atomicInteger.compareAndSet(0,1);//0!=5,不變
		atomicInteger.compareAndSet(2,3);//2!=5,不變
		atomicInteger.compareAndSet(3,4);//3!=5,不變
		atomicInteger.compareAndSet(5,2);//5==5,更新為2
		atomicInteger.compareAndSet(0,4);//0!=2,不變

		System.out.println("result:"+atomicInteger.get());

	}
}

輸出結果是:result:2

原始碼:

	/**使用 Unsafe.compareAndSwapInt()進行更新*/
		private static final Unsafe unsafe = Unsafe.getUnsafe();
		private static final long valueOffset;
		/**靜態程式碼塊對valueOffset進行初始化*/
		static {
			try {
				valueOffset = unsafe.objectFieldOffset
						(java.util.concurrent.atomic.AtomicInteger.class.getDeclaredField("value"));
			} catch (Exception ex) { throw new Error(ex); }
		}
		/**使用volatile修飾value,保證value值的一致性*/
		private volatile int value;

		/**
		 * 有參構構造方法
		 */
		public AtomicInteger(int initialValue) {
			value = initialValue;
		}

		/**
		 * 無參構造方法
		 */
		public AtomicInteger() {
		}

		/**
		 * 獲取值
		 */
		public final int get() {
			return value;
		}

		/**
		 * 設定值
		 */
		public final void set(int newValue) {
			value = newValue;
		}

		/**
		 * 最終設定為給定值。
		 */
		public final void lazySet(int newValue) {
			unsafe.putOrderedInt(this, valueOffset, newValue);
		}

		/**
		 * 原子地設定為給定值並返回舊值。
		 *
		 */
		public final int getAndSet(int newValue) {
			return unsafe.getAndSetInt(this, valueOffset, newValue);
		}

		/**
		 *
		 * 若當前值等於期望的值,則更新給定的更新值
		 *
		 */
		public final boolean compareAndSet(int expect, int update) {
			return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
		}
		
		

		/**
		 *自增1
		 *
		 */
		public final int incrementAndGet() {
			return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
		}

		/**
		 * 自減1
		 *
		 */
		public final int decrementAndGet() {
			return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
		}

		/**
		 * 增加給定的值
		 *
		 */
		public final int addAndGet(int delta) {
			return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
		}

		/**
		 * 更新並返回原先的值
		 */
		public final int getAndUpdate(IntUnaryOperator updateFunction) {
			int prev, next;
			do {
				prev = get();
				next = updateFunction.applyAsInt(prev);
			} while (!compareAndSet(prev, next));
			return prev;
		}

		/**
		 * 返回更新之後的值
		 */
		public final int updateAndGet(IntUnaryOperator updateFunction) {
			int prev, next;
			do {
				prev = get();
				next = updateFunction.applyAsInt(prev);
			} while (!compareAndSet(prev, next));
			return next;
		}

原子包還有以下,用法基本一樣。

cas中的ABA問題

  CAS可以有效的提升併發的效率,但同時也會引入ABA問題。

  如執行緒1從記憶體X中取出A,這時候另一個執行緒2也從記憶體X中取出A,並且執行緒2進行了一些操作將記憶體X中的值變成了B,然後執行緒2又將記憶體X中的資料變成A,這時候執行緒1進行CAS操作發現記憶體X中仍然是A,然後執行緒1操作成功。雖然執行緒1的CAS操作成功,但是整個過程就是有問題的。比如連結串列的頭在變化了兩次後恢復了原值,但是不代表連結串列就沒有變化。

  所以JAVA中提供了AtomicStampedReference/AtomicMarkableReference來處理會發生ABA問題的場景,主要是在物件中額外再增加一個標記來標識物件是否有過變更。

 

相關文章