理解CAS

理解CAS

什么是CAS

Compare And Swap 比较并交换

JUC 有两大核心:CAS 和 AQS

  • CAS是java.concurrent.automic包的基础
  • AQS是java.concurrent.locks包的基础

在这里插入图片描述

  • CAS是一条并发原语
  • 判断值是否到达预期值,如果到达了预期值,就更新
    • 如果预期值是5,更新值是9,那么如果该数据到达了5,就更新为9
  • 过程是原子性的
  • 调用sun.msic.Unsafe类中的方法
  • 因为java是没有办法操作硬件,但是c++可以操作硬件,于是通过Unsafe这个类中的元素操作C++进而操作硬件

为什么会出现CAS

说明:

  • 线程

因为,线程的创建与销毁是非常消耗时间的,而线程的执行时间是非常短的,所以出现了线程池,只需要创建一次线程,下次使用的时候,就可以直接使用线程池中的线程了

  • synchronized锁

之前就谈论过,表示synchronized是不太友好的操作,在执行到锁时,会经历用户态与内核态的转变,非常耗时,而synchronized是一个很小的区域,就是细粒度很高的操作,而为了这一点点的操作去耗时,是非常不划算的

因此出现了CAS

通过AtomicInteger介绍CAS

在这里插入图片描述

自己的方法调用

atomicInteger.getAndIncrement();

AtomicInteger类

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}


public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

通过unsafe类获取地址偏移量,但是具体是如何获得的,应该是在c++中操作的,在java中是没有写的

Unsafe类

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;
}
  • var 5 是通过AtomicInteger类和地址偏移量获得的值

  • 循环比较,通过地址(var1 ,var2)得到的值与car是否相等,如果相等就让,var5 = var5 + var4

  • var4 = 1;是传入的数值

  • 采用的叫自旋锁

  • 因为是操作内存的,所以调度是非常快的

上述最后是调用的compareAndSwapInt()方法

AtomicInteger自己也对上面的方法进行了封装

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

我们也可以直接调用这个类

缺点:

1、循环耗时

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

3、会产生ABA问题

CAS:ABA问题

什么是ABA问题

就是线程A,先获得了number的值 = 1,然后准备操作,但是没有操作 ,

线程B ,也拿到了number的值,然后将值 = 3 ,然后再次操作, number = 1,改回去了

但是线程A却什么都不知道

解决CAS问题

使用AtomicStampedReference,含有时间戳的类,也可以叫版本号

每次修改都需要对版本 + 1,这样就可以防治ABA问题

public class CasTest {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1);

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("线程a将值改变了");
            System.out.println(atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("线程a将值改回来了");
            System.out.println(atomicStampedReference.compareAndSet(2, 1,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
        },"a").start();


        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(atomicStampedReference.compareAndSet(1, 5
                    , stamp, stamp + 1));
        },"b").start();
    }
}
  • 线程B先拿到了版本,但是没有操作,延迟了2秒
  • 线程B在这期间,时间为1秒的时候拿到了版本,这个时候,每次执行都+1,这样线程a修改了值后,线程b就不再去对已经修改过的值进行操作了
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值