并发编程整理笔记13-CAS

本文介绍了CAS(Compare and Swap)的概念及其在Java中的应用,如Atomic系列的实现。同时,讨论了CAS的缺点,包括ABA问题和长时间自旋可能带来的资源消耗。针对ABA问题,提出了加锁和使用AtomicStampedReference作为解决方案。
摘要由CSDN通过智能技术生成

文章目录

CAS

什么是CAS
compareandswap:比较并更新
实现:从某一内存上取值V,和预期值A进行比较,如果内存值V和预期值A的结果相等,那么我们就把新值B更新到内存,如果不相等,那么就重复上述操作直到成功为止

CAS能做什么

  • 解决多线程安全问题:执行操作的时候会进行CAS操作,当发现当前值和预期值相符,会进行下一步操作,当不相符,此次操作失败

CAS在Java中的应用:Atomic系列就是使用CAS实现的

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;
    
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

CAS的缺点:ABA问题 ;长时间自旋转消耗资源

ABA问题

例子:

  1. 两个线程1,2进行自增操作,线程1,线程2同时获取当前的值A,
  2. 线程1比较快,在线程2操作前,进行了两次操作,第一次将A改为B,第二次将B又改为A
  3. 线程2操作的时候,发现当前值是A,符号预测,就会进行操作
  • 长时间自旋
    如果一个线程每次获取的值都被其他线程修改,那就会一直进行自旋,直到成功为止,这样会十分消耗资源

ABA的安全隐患

一般情况下ABA并不会出现什么问题,但是涉及引用的时候就回出现问题
例子:用链表实现一个栈,初始化向栈中压入B、A两个元素,栈顶执行A元素

  1. 线程1想将栈顶换成B,但他获取栈顶A之后,被线程2打断
  2. 线程2将A B弹出,然后压入C D
  3. 线程1继续执行,比较A相同,栈顶指向B,但是B已经被弹出

ABA问题的解决

  • 加锁
  • 原子引用

原子引用:带版本号的原子操作
JUC 包提供了一个带有时间戳的原子引用类 AtomicStampedReference 来解决该问题,它通过控制变量的版本来保证 CAS 的正确性。

public class CASDemo {

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

        new Thread(()->{
            // 获得版本号
            int stamp = atomicInteger.getStamp();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 后两个参数:期望的版本号
            atomicInteger.compareAndSet(0,2,
                    atomicInteger.getStamp(),atomicInteger.getStamp()+1);

            atomicInteger.compareAndSet(2,0,
                    atomicInteger.getStamp(),atomicInteger.getStamp()+1);
        },"线程1").start();

        new Thread(()->{
            // 获得版本号
            int stamp = atomicInteger.getStamp();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicInteger.compareAndSet(0, 6,
                    stamp, stamp+1);
        },"线程2").start();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值