四时宝库

程序员的知识宝库

Java面试都在问的CAS,你还没掌握吗?

一、什么是CAS

CAS(Compare and Swap)是处理并发问题的常用技术之一,它解决了线程安全和数据一致性问题。

CAS 是一种乐观锁机制,它是通过底层硬件提供的原子操作指令实现的,能够保证在多线程同时访问同一变量时,只有一个线程可以修改该变量的值。

CAS操作通常基于三个参数:内存位置V、期望值A和新值B。

CAS操作的执行过程如下:

1.首先,读取内存位置V的当前值,并将其与期望值A进行比较。

2.如果两者相等,则将内存位置V的值修改为新值B,并返回true,表示修改成功。

3.如果两者不相等,则不作修改,并返回false,表示修改失败。

简单来说就是:如果当前内存位置V的值等于期望值A,则将其修改为新值B;否则不做任何操作。

二、Java中的CAS

在 Java 中,CAS 操作通常是通过 sun.misc.Unsafe 类来提供支持的。Unsafe 类提供了一组底层操作,可以直接访问内存并执行一些原子性操作。如下是 Unsafe 中的三个 native 关键字修饰的CAS方法,Java 中的 CAS 操作都是通过这三个方法实现的。

public final class Unsafe {
    ......
    
    // Object 的 CAS操作
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    // int 的 CAS操作
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    // long 的 CAS操作
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
          // 自旋调用 CAS操作
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
          // 自旋调用 CAS操作
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6;
    }

    public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
          // 自旋调用 CAS操作
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }

    public final long getAndSetLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
          // 自旋调用 CAS操作
        } while(!this.compareAndSwapLong(var1, var2, var6, var4));

        return var6;
    }

    public final Object getAndSetObject(Object var1, long var2, Object var4) {
        Object var5;
        do {
            var5 = this.getObjectVolatile(var1, var2);
          // 自旋调用 CAS操作
        } while(!this.compareAndSwapObject(var1, var2, var5, var4));

        return var5;
    }
    ......
}

例如 AtomicInteger 源码中使用 CAS 的部分源码附带说明如下:

public class AtomicInteger extends Number implements java.io.Serializable {

    private static final long serialVersionUID = 6214790243416807050L;

    // 使用 Unsafe.compareAndSwapInt 来更新值
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    // 获取 value 属性在对象中的偏移量
    private static final long valueOffset;

    static {
        try {
            // 获取 value 属性在对象中的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    // 使用 volatile 修饰 value 属性,保证线程之间的可见性
    private volatile int value;

    ......

    // 自增并返回自增前的值
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    // 自增并返回自增后的值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    ......
}

三、注意事项

需要注意的是,虽然CAS操作具有很好的互斥性和原子性,但也存在一些需要注意的问题:

  • 自旋次数过多会影响性能:当多个线程访问同一变量时,如果修改失败,CAS操作会进行自旋等待,以期望其他线程修改完成后再次尝试。如果自旋次数过多,会浪费大量的CPU资源。
  • 受限于底层硬件平台:由于CAS操作是基于底层硬件平台提供的支持,因此受限于硬件平台的类型和性能,不同的硬件平台可能对CAS操作的具体实现略有差别。
  • 无法避免ABA问题:当变量从A -> B -> A后,如果期望值A没有变化,CAS操作仍然会成功,但实际上变量的值已经发生了变化。如果你想了解更多关于ABA问题,请查看我的另一篇文章【 还不知道ABA问题?大厂面试Java必提的问题

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接