CAS

CAS介绍

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(乐观锁)的方式实现业务,具体要看业务场景,若是重试的代价很小,那用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);
    }

}

CAS缺点 - ABA问题

进程P1读取了一个数值A
P1被挂起(时间片耗尽、中断等),进程P2开始执行
P2修改数值A为数值B,而后又修改回A
P1被唤醒,比较后发现数值A没有变化,程序继续执行。

解决思路:在每次更新的同时附上版本号,如:1A -> 2B -> 3A。JDK1.5开始新增的java.util.concurrent.atomic.AtomicStampedReference就是一种实现方式。atom

Redis中的CAS

Redis能够使用WATCH来实现对事务中键(能够是多个键)的监视,若是至少有一个键在EXEC执行前被改动,那么整个事务都会被取消, EXEC返回nil-reply来表示事务已经失败。线程

具体参见:Redis - 事务(transactions)code

相关文章
相关标签/搜索