CAS - Compare And Swap (Compare And Set, Check And Set)html
wikipedia的描述以下:java
比較並交換(compare and swap, CAS),是原子操做的一種,可用於在多線程編程中實現不被打斷的數據交換操做,從而避免多線程同時改寫某一數據時因爲執行順序不肯定性以及中斷的不可預知性產生的數據不一致問題。 該操做經過將內存中的值與指定數據進行比較,當數值同樣時將內存中的數據替換爲新的值。
Java在sun.misc.Unsafe
類庫裏面的CAS實現。redis
如下源碼摘自java.util.concurrent.locks.AbstractQueuedSynchronizer
。編程
/** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this //this: 當前對象 //stateOffSet: 偏移量,聲明在下面已貼出 //expect: 期待值 //update: 更新值 //若是stateOffSet的值與expect相等,則將stateOffset的值更新爲update;並返回true。 //不然不更新,並返回false。 return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } ...... private static final long stateOffset; static { try { stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state")); ...... } catch (Exception ex) { throw new Error(ex); } }
在多線程狀況下,能夠採用同步阻塞(悲觀鎖)或CAS(樂觀鎖)的方式實現業務,具體要看業務場景,若是重試的代價很小,那用CAS是合適的,但若是每次重試都須要花費大量的時間或資源,那應該採用同步方式。多線程
如下是2種方式的簡單舉例:this
class MyLock { private boolean locked = false; public synchronized boolean lock() { if(!locked) { locked = true; return true; } return false; } }
public static class MyLock { private AtomicBoolean locked = new AtomicBoolean(false); public boolean lock() { return locked.compareAndSet(false, true); } }
進程P1讀取了一個數值A P1被掛起(時間片耗盡、中斷等),進程P2開始執行 P2修改數值A爲數值B,而後又修改回A P1被喚醒,比較後發現數值A沒有變化,程序繼續執行。
解決思路:在每次更新的同時附上版本號,如:1A -> 2B -> 3A
。JDK1.5開始新增的java.util.concurrent.atomic.AtomicStampedReference
就是一種實現方式。atom
Redis能夠使用WATCH
來實現對事務中鍵(能夠是多個鍵)的監視,若是至少有一個鍵在EXEC
執行前被改動,那麼整個事務都會被取消, EXEC
返回nil-reply
來表示事務已經失敗。線程
具體參見:Redis - 事務(transactions)code