JUC包中的CAS

什么是CAS

  • 比较并交换(CompareAndSwap)

比较当前工作内存的值和主内存中的值,如果相同则执行规定中的操作,否则继续比较直到主内存和工作内存中的值一致为止。

  • 示例代码
public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(5);

    // 第一个参数为expect:期望值 第二个参数为update:更新值
    boolean b = atomicInteger.compareAndSet(5, 2019);
    log.info("是否交换成功:{},现在的值{}", b, atomicInteger.get());

    boolean bool = atomicInteger.compareAndSet(5, 2020);
    log.info("是否交换成功:{},现在的值{}", bool, atomicInteger.get());
}

/*
结果:
是否交换成功:true,现在的值2019
是否交换成功:false,现在的值2019
*/

如果线程工作内存的期望值跟物理内存的真实值一样,就修改为更新值,本次操作为true

如果不一样,本次修改失败,本次操作为false

CAS的缺点

  • 循环时间长,开销很大
  • 只能保证一个共享变量的原子操作
  • ABA问题

什么是ABA问题?

CAS算法实现一个重要前提,需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差内会导致数据的变化

比如说:
一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。

尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。

ABA问题解决

AtomicStampedReference使用版本号initialStamp解决ABA问题

log.info("-------------------------------模拟ABA问题-------------------------------------");

AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
ThreadUtil.newThread(() -> {
    atomicReference.compareAndSet(100, 101);
    atomicReference.compareAndSet(101, 100);
    log.info("ABA完成");
}, "t1").start();

ThreadUtil.newThread(() -> {
    // -- t2线程睡眠1s,保证t1线程ABA操作完成 --
    ThreadUtil.sleep(1, TimeUnit.SECONDS);
    boolean b1 = atomicReference.compareAndSet(100, 2019);
    log.info("t2操作是否成功:{},当前值为:{}", b1, atomicReference.get());
}, "t2").start();

ThreadUtil.sleep(3, TimeUnit.SECONDS);

log.info("----------------------------解决ABA问题----------------------------------------");

AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
ThreadUtil.newThread(() -> {
    int stamp = atomicStampedReference.getStamp();
    ThreadUtil.sleep(1, TimeUnit.SECONDS);
    boolean b = atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1);
    log.info("t3第一次操作是否成功:{},当前值为:{}", b, atomicStampedReference.getReference());
    boolean b1 = atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
    log.info("t3第二次操作是否成功:{},当前值为:{}", b1, atomicStampedReference.getReference());
}, "t3").start();

ThreadUtil.newThread(() -> {
    int stamp = atomicStampedReference.getStamp();
    ThreadUtil.sleep(3, TimeUnit.SECONDS);
    boolean b = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
    log.info("t4操作是否成功:{},当前值为:{}", b, atomicStampedReference.getReference());
}, "t4").start();

/*
执行结果:
15:00:25.570 [main] INFO com.zckj.controller.OrderZkController - -------------------------------模拟ABA问题-------------------------------------
15:00:25.625 [t1] INFO com.zckj.controller.OrderZkController - ABA完成
15:00:26.626 [t2] INFO com.zckj.controller.OrderZkController - t2操作是否成功:true,当前值为:2019
15:00:28.626 [main] INFO com.zckj.controller.OrderZkController - ----------------------------解决ABA问题----------------------------------------
15:00:29.629 [t3] INFO com.zckj.controller.OrderZkController - t3第一次操作是否成功:true,当前值为:101
15:00:29.629 [t3] INFO com.zckj.controller.OrderZkController - t3第二次操作是否成功:true,当前值为:100
15:00:31.629 [t4] INFO com.zckj.controller.OrderZkController - t4操作是否成功:false,当前值为:100
*/

该知识点从b站学习,感谢共享知识的活雷锋,感谢尚硅谷阳哥!!!视频地址想看的小伙伴可以点击连接自行获取相关知识

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值