最近看java并发库的源代码,经常看到Unsafe及对应的CAS方法,现在来探究下CAS

什么是CAS?

CAS意指compare and swap,即比较并交换。它代表在java中一类方法,原子的实现数据交换并且判断数据内容是否符合预期。

通常有三个参数,需要交换的对象A,我们预期对象A的值为B,即将交换的A的值C,在单线程中我们只需要如下代码即可:

Object a;//最初的对象
Object b;//预期a的值
Object c;//最终a的值
if(a==b){
    a = c;
}

 但在多线程中,上面的代码会出现严重的问题,如果线程1经判断a==b,准备执行a=c时,cpu切换到线程2,且将a改成了d,

    当cpu重新切换到线程1,此时a已经不等于b了,也即是执行a=c的前提条件已经不满足;

 因此我们需要对上面的操作原子的进行,当然我们可以对程序块加锁实现,这里我们讨论另一种实现方法,即使用Unsafe类CAS方法

Unsafe类?

这是sun.misc包中一个类,这并不是一个正常的类,没有办法使用正常的new Unsafe()来创建对象,其虽然提供了静态的getUnsafe方法,但仍然无法使用,调用时会报java.lang.SecurityException: Unsafe的错误。当然我们可以通过反射去绕过限制,但除非必要在正式项目中最好不要使用,拿来探究jvm确实是很有用的类。

private Unsafe() {
    }

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if(var0.getClassLoader() != null) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

  

例如:可以直接操作内存,下面三个方法分别申请内存,扩充内存及释放内存。

public native long allocateMemory(long paramLong);
public native long reallocateMemory(long paramLong1, long paramLong2);
public native void freeMemory(long paramLong);

CAS方法:以compareAndSwapInt为例,var1是需要更新的变量,var2是相对object对象的偏移量(一般为var1某个整形属性的偏移量),var4是期望值,var5是整形属性的新值。

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

 

查看一些文章,在linux中的实现这样一个函数:*ptr 与old 相比较,如相同,则new写入ptr

cmpxchg(void *ptr, unsigned long old, unsigned long new);

 

下面看一个demo,在这个例子中,我们创建了一个Unsafe实例,并通过它直接创建了一个UserToken对象,我们发现创建过程中并未进行初始化,之后测试了 compareAndSwapInt方法,当age==10时,设置age=30成功,当age!=10时(此时age=30),设置age=20失败。

  

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * Created by liushuai on 17/5/2.
 */
public class UserToken {
    private String name = "test";
    private int age =12;
    private UserToken(){
        name = "1234";
        age = 5;
    }

    public static void main(String[] args) throws Exception {
        //获取Unsafe实例
        Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        //绕过UserToken构造方法及初始化,创建对象
        UserToken t = (UserToken)unsafe.allocateInstance(UserToken.class);
        //并未初始化,所以 age==0
        System.out.println(t.age);  //0
        t.age=10;
        System.out.println(t.age);  //10
        //获取age属性相对对象在内存中的偏移量offset
        Field ageFiled = t.getClass().getDeclaredField("age");
        long offset = unsafe.objectFieldOffset(ageFiled);
        //修改age属性值
        boolean flag = unsafe.compareAndSwapInt(t,offset,10,30);
        System.out.println(flag);//true
        System.out.println(t.age);//30
        flag = unsafe.compareAndSwapInt(t,offset,10,20);
        System.out.println(flag);//false
        System.out.println(t.age);//30
    }

}

  

AtomicInteger中CAS应用

此类位于java.util.concurrent.atomic包下,提供对整形数字的原子操作。

// setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

  从代码中我们看到,AtomicInteger类持有一个静态final修饰的Unsafe对象,并在静态块中初始化获取了此类中私有变量value的对象偏移量,value即是对象包含的真正的整形数值,由volatile关键字修饰,确保对value的修改对所有线程可见,valueOffset即value值在内存中相对于AtomicInteger实例对象的偏移量。根据上面的例子可知,可以使用unsafe根据偏移量获取和修改value的值,而且操作保证原子性。

/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);//调用unsafe的cas方法
    }

  

以及自增方法:调用链为

getAndIncrement---->compareAndSet----->unsafe.compareAndSwapInt
/**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

  

AQS中状态的变更

AQS全称AbstractQueuedSynchronizer,位于java.util.concurrent.locks包中一个抽象类,这个类提供许多公共的方法供子类使用,其中存储了一个链式结构的双向链表队列,用来存储被阻塞的线程,并且将这部分线程封装成Node对象,并保存了线程的等待状态waitStatus。

/** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;//代表线程已被取消
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1; //代表后续线程将被唤醒
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2; //代表线程等待某一条件
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;//代表后续结点会传播唤醒的操作,共享模式下起作用
        volatile int waitStatus;
        private volatile int state;

  

我们暂不讨论AQS实现原理,只探究下它的状态变换机制,及Node替换。如下面我们看到的,对于waitStatus,state,head,tail,nex等变量,都使用了volatile修饰,以确保其修改对所有线程可见,初始化时获取Unsafe实例,并在static静态块中初始化获取以上变量在内存中相对于对象的偏移量。

 

private static final Unsafe unsafe = Unsafe.getUnsafe();  //获取Unsafe对象
private static final long stateOffset; 
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;

static {   //获取属性偏移量
    try {
        stateOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        headOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
        tailOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
        waitStatusOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("waitStatus"));
        nextOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("next"));

    } catch (Exception ex) { throw new Error(ex); }
}

/**
     * 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 <tt>volatile</tt> read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return 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
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update); //状态变换
    }

   /**
     * CAS head field. Used only by enq.
     */
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);  //头结点设置
    }

    /**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update); //尾节点替换
    }

    /**
     * CAS waitStatus field of a node.
     */
    private static final boolean compareAndSetWaitStatus(Node node,    //等待状态更新
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
    }

    /**
     * CAS next field of a node.
     */
    private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }

  

因此我们总结下CAS的应用:

1 获取Unsafe的实例

2 初始化变量的偏移量

3 变量由volatile关键字修饰