CAS(Compare And Set)

Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

 

悲观锁与乐观锁

悲观锁,认为数据发生并发冲突的概率很大,所以读操作之前就上锁。synchronized关键字,ReentrantLock 都是悲观锁的典型例子。

对于乐观锁,认为数据发生并发冲突的概率比较小,所以读操作之前不上锁。等到写操作的时候,再判断数据在此期间是否被其 他线程修改了。如果被其他线程修改了,就把数据重新读出来,重复该过程;如果没有被修改,就写回去。判断数据是否被修改,同时写 回新值,这两个操作要合成一个原子操作,也就是CAS(Compare And Set)。

 

原子更新基本类型类

使用原子的方式更新基本类型,Atomic包提供了以下3个类。

❑ AtomicBoolean:原子更新布尔类型。

❑ AtomicInteger:原子更新整型。

❑ AtomicLong:原子更新长整型。

以上3个类提供的方法类似以AtomicInteger为例进行讲解,AtomicInteger的常用方法如下。

❑ int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。

❑ boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。

❑ int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值。

❑ void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

❑ int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。

public class AtomicIntegerTest {
    static AtomicInteger ai = new AtomicInteger(1);
    public static void main(String[] args) {
        System.out.println(ai.getAndIncrement());
        System.out.println(ai.get());
    }
}

  

Unsafe的CAS

CAS函数,其实是封装的Unsafe类中的一个native函 数,如下所示。

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

AtomicInteger封装过的compareAndSet有两个参数。

第一个参数e xpect是指变量的旧值(是读出来的值,写回去的时候,希望没有被其 他线程修改,所以称为expect);

第二个参数update是指变量的新值 (修改过的,希望写入的值)。

当expect等于变量当前的值时,说明 在修改的期间,没有其他线程对此变量进行过修改,所以可以成功写 入,变量被更新为update,返回true;否则返回false。

Unsafe类是整个Concurrent包的基础,里面所有函数都是native 的。具体到compareAndSwapInt函数,如下所示。

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

该函数有4个参数。在前两个参数中,第一个是对象(也就是 AtomicInteger 对象),第二个是对象的成员变量(也就是AtomictInteger里面包的int变量value),后两个参数保持不变。 要特别说明一下第二个参数,它是一个long型的整数,经常被称为xxxOffset,意思是某个成员变量在对应的类中的内存偏移量(该变 量在内存中的位置),表示该成员变量本身。在Unsafe中专门有一个函数,把成员变量转化成偏移量,如下所示。

public native long objectFieldOffset(Field var1);

所有调用CAS的地方,都会先通过这个函数把成员变量转换成一个 Offset。以AtomicInteger为例:

CAS(Compare And Set)

 

 从上面代码可以看到,无论是Unsafe还是valueOffset,都是静态 的,也就是类级别的,所有对象共用的。 在转化的时候,先通过反射(getDeclaredField)获取value成员 变量对应的Field对象,再通过objectFieldOffset函数转化成valueOffset。此处的valueOffset就代表了value变量本身,后面执行CAS操作 的时候,不是直接操作value,而是操作valueOffset。

 

 

 

 

参考: Java并发编程的艺术 7.1 原子更新基本类型类

  Java并发实现原理:Atomic类

 

上一篇:联系五——链式栈


下一篇:一年工作经验的我,居然被问到了CAS