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