Java中CAS操作本身怎么保证原子性及其原理分析

Java中CAS操作是怎么保证原子性的?

在学习Java多线程时我们会接触到CAS这样一个概念,CAS其实就是Compare And Swap的一个缩写。Compare And Swap,顾名思义就是比较并交换,其实就是把当前值与你预期的值进行一个比较,如果一样再进行修改,否则不修改并返回失败。这里我解释一下,当前值与预期值的一个概念:多线程环境下很可能发生当我拿到一个值还没修改的时候,别的线程已经把这个值修改掉了,这样就会引发线程安全问题,所以CAS操作就是在修改前用一个当前值和期望值来进行比较,只有相等,即没有其他线程修改过这个值的时候(也可能发生一个线程改了,又有一个线程又改回来的情况,CAS无法感知到这种操作,它还是会认为数据没有发生变化而进行修改。这种情况可以利用AtomicStampedReference和AtomicMarkableReference解决,两者根据应用场景不同一定区别,这里不做阐述),才进行修改,从而保证操作的原子性。

可以看到compareAndSet方法中的expect(期望值),update(修改的值)。

CAS本身操作的原子性

那么CAS这个操作本身又是怎么保证原子性的呢?可以想象:比较——修改这是两个动作,可能我比较的时候它是一样的,当我修改的时候它却被别的线程修改了。这就涉及到CAS本身这个操作是原子的,也就是不被其他线程所干扰的。这是利用CPU的原语来实现的。我们知道Java方法无法直接访问底层系统,需要通过本地(Native)方法来访问,Unsafe相当于一个桥梁,基于该类可以连接底层的操作系统直接操作特定的内存数据,Unsafe类存在sun.misc包中,其内部方法操作可以像C指针一样直接操作内存,因此Java中的CAS操作的执行依赖于Unsafe类的方法。(Unsafe类很重要!不知道的同学可以多去了解,建议自己利用反射获取unsafe对象自己写一些东西,比如自己写一个原子类)

进入compareAndSet方法我们发现他返回的是unsafe的compareAndSwapInt方法:

进入Unsafe类的native方法compareAndSwapInt,既然是native方法,那就不涉及到Java的代码了,调用UnSafe类中的CAS方法,也就是这个本地native方法,JVM会帮我们实现出CAS汇编指令,这是一种完全依赖于硬件的功能,通过它实现了原子操作,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致的问题,也就是说CAS是线程安全的。

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

变量valueOffset

表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的,这个变量我们在自己利用unsafe对象写一些操作的时候会用到,其实Compare操作也是一个内存比较的过程。

附上一个自己写的原子类:

import sun.misc.Unsafe;

import java.lang.reflect.Field;

class MyAtomicInteger {
    private volatile int value;
    private static final long valueOffset;
    static final Unsafe UNSAFE;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);   //这个成员变量就是Unsafe类型
            UNSAFE = unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new Error(e);
        }
    }

    static {
        try {
            //获取value的偏移量,用于Unsafe直接访问该属性
            valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();;
            throw new RuntimeException(e);
        }
    }

    public int getValue() {
        return value;
    }

    public void increment(int amount){
        while (true){
            int prev = this.value;
            int next = prev - amount;
            //CAS
            if (UNSAFE.compareAndSwapInt(this,valueOffset,prev,next)){
                break;
            }
        }
    }
}

 

 

以上只是个人学习中一些感悟与分享,如有错误欢迎指正与交流。

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值