Java面试之CAS

Java面试之CAS,第1张

Java面试之CAS 面试时候经常被面试官灵魂三问:什么是CAS?原理是什么?有什么弊端?

 接下来,让我们给他说道说道。

进入正题:

1. 什么是 CAS?

CAS(Compare-And-Swap)是比较并交换的意思,它是一条 CPU 并发原语,用于判断内存中某个值是否为预期值,如果是则更改为新的值,这个过程是原子的。

 CAS机制当中使用了3个值:内存地址V,旧的预期值A,计算后要修改的新值B

  1.  两个线程同时对内存值V进行 *** 作,V初始值为1
  2. 线程1、线程2都对V加1计算,预期值A=1,新值B=2
  3. 线程2先提交,预期值A==V,更新成功,将V更新为2
  4. 线程1提交时4,发现预期值A=1,V=2,A!=V,提交失败,重新获取内存值V=2
  5. 线程1自旋,V=2,A=2,B=3,重新比较A==V成立,然后更新V=3,最后V=3结束

总结

更新一个变量的时候,只有当变量的预期值 A 和内存地址 V 中的实际值相同时,才会将内存地址 V 对应的值修改为 B,这个 *** 作就是CAS

2. CAS 基本原理

CAS 主要包括两个 *** 作:Compare和Swap。

CAS 是一条 CPU 的原子指令,执行必须是连续的,执行过程中不允许被中断。

JDK 是在 1.5 版本后才引入 CAS *** 作,在sun.misc.Unsafe这个类中定义了 CAS 相关的方法

    
    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);
3. Java 中的应用

CAS主要封装在java并发编程工具包中,java.util.concurrent包

  AtomicInteger 类解决 i++ 非原子性问题,通过volatile 关键字和 CAS *** 作来实现

查看下源码:

    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }

    

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
4. 弊端
  • 典型的ABA 

ABA 是 CAS *** 作的一个经典问题,一个变量初始值为 A,修改为 B,然后又修改为 A,这个变量实际被修改过了,但是 CAS *** 作无法感知到。

解决这个问题也很简单,只要在变量前加版本号,每次变量更新了就把版本号加1

 

  • 自旋开销

CAS 出现冲突后就会开始重复尝试,即自旋 *** 作,如果资源竞争非常激烈,自旋长时间不能成功就会给 CPU 带来非常大的开销。

优化:限制自旋的次数,避免过度消耗 CPU;

  • 只能保证单个变量的原子性

当对一个共享变量执行 *** 作时,可以使用 CAS 来保证原子性,如果要对多个共享变量进行 *** 作时,CAS 是无法保证原子性的,比如需要将 i 和 j 同时加 1:

i++;j++;

优化:

1、使用 synchronized 进行加锁;

2、将多个变量 *** 作合成一个变量 *** 作。AtomicReference 类来保证引用对象之间的原子性,把多个变量放在一个对象里来进行CAS *** 作

 AtomicReference 关键方法:

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4670740.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存