Java面试题之CAS

CAS: compare and swap(比较和交换),以AtomicInteger为例,讲解CAS的使用与原理。以代码为例:

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,2019)+" \t current  "+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,2014)+" \t current  "+atomicInteger.get());
    }
}

atomicIngeter初始化为5,调用对象的compareAndSet方法来对比当前值与内存中的值,是否相等,相等则更新为2019,不相等则不会更新,compareAndSet方法返回的是boolean类型。程序执行的结果如下:

第一次调用,内存中的值是5,通过对比,相等则更新为2019,第二次调用时,内存重点的值已经更新为2019,不相等则返回false,并且不更新内存中的值。

 

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 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;

1,UnSafe是CAS的核心类,由于Java方法无法直接访问底层,需要通过本地(native)方法来访问,UnSafe相当于一个后面,基于该类可以直接操作特定的内存数据,UnSafe类在于rt.jar中的sun.misc包中,其内部方法操作可像C的指针一样直接操作内存,因此Java中CAS操作的核心依赖于UnSafe类的方法。

注意UnSafe类中的所有方法都是navite修饰的,也就是说UnSafe类中的方法都是直接调用操作系统底层资源执行响应的任务

现在看看UnSafe中的具体实现

2,变量valueOffset便是改变量在内存中的编译地址,因为UnSafe就是根据偏移地址获取数据的

 /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

3,变量value使用volatile修饰,保证了多线程直接的可见性。

下面看看UnSafe类中getAndAddInt方法的具体实现。

 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

var1 AtomicInteger对象本身,var2 该对象值的引用地址,var4 需要变更的数值  var5 是用var1 var2找出内存中真实的值

用改对象当前的值与var5比较,如果相同,更新var5的值并且返回true,如果不同,继续获取值然后比较,直到更新完成。

 

进一步举例解释说明:

假设线程A和线程B两个线程同时执行getAndAddInt操作(分别在不同的cpu上):

1,AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据JMM模型,线程A和线程B各自持有一份值为3的value的副本,分别到各自的工作内存。

2,线程A通过getIntVolatile(var1,var2) 拿到value值3,这时线程A被挂起。

3,线程B通过getIntVolatile(var1,var2)拿到value值3,此时刚好线程B没有被挂起执行compareAndSwapInt方法比较内存中的值也是3 成功修改内存的值为4 线程B执行完,一切OK

4,这时线程A恢复,执行compareAndSawpInt方法比较,发现自己手里的数据的值和内存中的数值4不一致,说明该值已被其他线程抢先一步修改了,那A线程修改失败,只能重新来一遍了,

5,线程A重新获取value值,因为变量value是volatile修饰,所以其他线程对他进行的修改,线程A总能看到,线程A继续执行compareAndSwapInt方法进行比较替换,直到成功。

UnSafe类中的compareAndSwapInt,是一个本地方法,该方法的实现于unsafe.cpp中。下面就是该方法的实现。

CAS的缺点:

1,循环时间长开销大

     从UnSafe类的getAndAddInt方法执行时,可以看到有个do while语句

     如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

2,只能保证一个共享变量的原子性

      当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无        法保证操作的原子性,这个时候就可以使用锁来保证原子性。

3,会引起ABA问题

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值