CAS 思想与 java 原子操作的实现

2022-06-27 12:40:26 浏览数 (1)

1. 概述

此前的文章中,我们介绍了 java 内存模型和 volatile 关键字以及 synchronized 关键字。

java 内存模型与 volatile 的实现

synchronized 的使用及实现原理

本文,我们来介绍保证并发安全的重要思想 — CAS。

2. CAS (Compare And Swap)

CAS (Compare And Swap)是并发系统中,实现原子操作和锁的常见思想。 顾名思义就是比较并交换,通过传入原值与需要更新的值,保证只有在待修改值与首个参数的值相等时才执行赋值操作,让其赋值为第二个参数,只要保证了整个过程的原子性,则使用者可以返回值判断并重试的方式保证并发环境下的安全性。 java 中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现 CAS,java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。 打开 Unsafe 类的源码可以看到,大量的方法都是 native 方法,这是因为这个类是 jvm 通过 C 实现的硬件操作来保证其原子性的原子操作,这里就不对其实现多做介绍了。

3. Unsafe 类与 CAS

Unsafe 是通过类中的以下三个方法来实现 CAS 的:

代码语言:javascript复制
public final native boolean compareAndSwapObject(
    Object o, long offset,Object expected, Object x);
public final native boolean compareAndSwapInt(
    Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(
    Object o, long offset,long expected,long x);

在 java8 中,Unsafe 类提供了以下方法实现 CAS 思想:

代码语言:javascript复制
 public final int getAndAddInt(Object o, long offset, int delta) {
     int v;
     do {
         //获取内存中最新值
         v = getIntVolatile(o, offset);
       //通过CAS操作
     } while (!compareAndSwapInt(o, offset, v, v   delta));
     return v;
 }

//1.8新增,方法作用同上,只不过这里操作的long类型数据
 public final long getAndAddLong(Object o, long offset, long delta) {
     long v;
     do {
         v = getLongVolatile(o, offset);
     } while (!compareAndSwapLong(o, offset, v, v   delta));
     return v;
 }

 //1.8新增,给定对象o,根据获取内存偏移量对于字段,将其 设置为新值newValue,
 //这是一个CAS操作过程,直到设置成功方能退出循环,返回旧值
 public final int getAndSetInt(Object o, long offset, int newValue) {
     int v;
     do {
         v = getIntVolatile(o, offset);
     } while (!compareAndSwapInt(o, offset, v, newValue));
     return v;
 }

// 1.8新增,同上,操作的是long类型
 public final long getAndSetLong(Object o, long offset, long newValue) {
     long v;
     do {
         v = getLongVolatile(o, offset);
     } while (!compareAndSwapLong(o, offset, v, newValue));
     return v;
 }

 //1.8新增,同上,操作的是引用类型数据
 public final Object getAndSetObject(Object o, long offset, Object newValue) {
     Object v;
     do {
         v = getObjectVolatile(o, offset);
     } while (!compareAndSwapObject(o, offset, v, newValue));
     return v;
 }

上面的代码中,通过 compareAndSwap 系列方法实现了原子的增减操作,例如 getAndAddInt 方法:

代码语言:javascript复制
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        //获取内存中最新值
        v = getIntVolatile(o, offset);
        //通过CAS操作
    } while (!compareAndSwapInt(o, offset, v, v   delta));
    return v;
}

通常,这样做的性能是要优于使用 synchronized 的,但如果资源竞争十分激烈的话,这个 for 循环可能换持续很久都不能成功跳出。

4. AtomicInteger 中的使用

下面,我们通过 AtomicInteger 的源码来介绍一下 Unsafe 类的使用:

代码语言:javascript复制
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 获取指针类Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    //下述变量value在AtomicInteger实例对象内的内存偏移量
    private static final long valueOffset;

    static {
        try {
           //通过unsafe类的objectFieldOffset()方法,获取value变量在对象内存中的偏移
           //通过该偏移量valueOffset,unsafe类的内部方法可以获取到变量value对其进行取值或赋值操作
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    //当前AtomicInteger封装的int变量value
    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    //获取当前最新值
    public final int get() {
        return value;
    }

    //设置当前值,具备volatile效果,方法用final修饰是为了更进一步的保证线程安全。
    public final void set(int newValue) {
        value = newValue;
    }

    //最终会设置成newValue,使用该方法后可能导致其他线程在之后的一小段时间内可以获取到旧值,有点类似于延迟加载
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    //设置新值并获取旧值,底层调用的是CAS操作即unsafe.compareAndSwapInt()方法
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    //如果当前值为expect,则设置为update(当前值指的是value变量)
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //当前值加1返回旧值,底层CAS操作
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    //当前值减1,返回旧值,底层CAS操作
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    //当前值增加delta,返回旧值,底层CAS操作
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //当前值加1,返回新值,底层CAS操作
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1)   1;
    }

    //当前值减1,返回新值,底层CAS操作
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    //当前值增加delta,返回新值,底层CAS操作
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta)   delta;
    }

    // ...
}

上面是一部分 AtomicInteger 类的源码,可以看到,在给出的源码中,并没有使用锁来保证并发安全性,而是直接调用了 Unsafe 类中的原子操作。 其他 Atomic 类也和 AtomicInteger 类似,最终通过直接调用 Unsafe 类实现了原子性。

5. 参考资料

《Java并发编程的艺术》。 《Java 编程思想》。 《Java高并发程序设计》。 《深入理解 jvm 虚拟机》。 https://blog.csdn.net/javazejian/article/details/72772470。 https://blog.csdn.net/javazejian/article/details/75043422。 https://blog.csdn.net/javazejian/article/details/76167357。 https://blog.csdn.net/u010862794/article/details/72892300。 https://www.cnblogs.com/waterystone/p/4920797.html。

0 人点赞