Cas简单理解

java里的CAS

什么是cas?

cas全称compare-And-Swap,简单理解就是比较更新,和内存中指定位置上的值进行比较,如果期望的值和自己的值一致,则把指定位置上的值替换为自己想要的值。他是sun.misc.Unsafe类下的native操作,操作的过程是原子操作。(Don‘’t talk, show the code!)

##下面是cas一个简单的应用(AtomicInteger相信大家都并不陌生)

public class LockDemo {
    AtomicInteger i = new AtomicInteger(0);
    private void add() {
        i.incrementAndGet();
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo demo = new LockDemo();
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    demo.add();
                }
            }).start();
        }
        Thread.sleep(2000L);
        System.out.printf("i=" + demo.i);
    }
}

下面是运行结果:
在这里插入图片描述
下面我们一起看看AtomicInteger是怎么实现原子操作的:

  1. AtomicInteger的原子加法(其实该类的几个方法原理都差不太多);
/**
  * Atomically increments by one the current value.
  *
  * @return the updated value
  */
 public final int incrementAndGet() {
     return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
 }

这个里面调用了unsafe的getAndAddInt方法,我们且去看一看这个方法:

  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
           //循环获取该对象在valueOffset上面的值
            var5 = this.getIntVolatile(var1, var2);
            //cas+1操作失败,重试获取最新值
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

其中的compareAndSwapInt就是cas操作的体现,下面是jdk源方法

  public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

cas应用广泛,比如可重入锁(ReentrantLock),信号量(Semaphore),监视器(CountDownLanch),线程栅栏(CyclicBarrier)都有它的踪迹。

2.下面我们尝试自己去写一个类似的加法,以便于深入理解CAS

public class LockDemo1 {
    volatile int value = 0;
    static Unsafe unsafe; //直接操作内存 修改对象 ,数组内存 强大的api
    private static long valueOffset = 0;

    static {
        try {
            //反射技术获取unsafe值
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
            //获取到value 属性偏移量 (用于定位value在内存中的地址)
            valueOffset = unsafe.objectFieldOffset(LockDemo1.class.getDeclaredField("value"));
        } catch (Exception var) {
            var.printStackTrace();
        }
    }

    private void add() {
        //i++ //JAVA三个步骤
        //CAS +循环 重试
        int current;
        do {
            current = unsafe.getIntVolatile(this, valueOffset);
            /**
             * public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
             * 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
             *
             * @param obj 需要更新的对象
             * @param offset obj中整型field的偏移量
             * @param expect 希望field中存在的值
             * @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
             * @return 如果field的值被更改返回true
             */
        } while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));//可能会失败

    }
    public static void main(String[] args) throws InterruptedException {
        LockDemo1 demo = new LockDemo1();
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    demo.add();
                }
            }).start();
        }
        Thread.sleep(2000L);
        System.out.printf("i =" + demo.value);
    }
}

其执行结果和上面的AtomicInteger 一模一样
在这里插入图片描述
3.Cas有没有什么缺点呢?
答案是有。比如上面的do while循环中 如果cas操作一直失败,那么循环就会一直处于重试;还有就是有名的ABA问题,线程T把资源的A值进行改变从A改为B再次A,如果另外的线程获取到的最新值还是A,那么问题就会产生,其中有名的解决方法就是修改的时候加入版本号的概念。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值