CAS如何实现原子性与ABA问题

CAS(Compare and Swap)

CAS 全称是 compare and swap(比较并且交换),是一种用于在多线程环境下实现同步功能的机制,其也是无锁优化,或者叫自旋,还有自适应自旋(说法不算准确,底层还是存在锁,后面会讲)。

由java到汇编来看CAS底层实现

//以AtomicInteger类的compareAndSet方法举例。

//其源码:
    //expect与旧值一致,则用update替换旧值
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

问题:CAS是原子性的吗?从上面的compareAndSet方法为例,需要先与旧值比较,然后再替换旧值,那是不是代表是两个操作呢?

下面从源码开始分析:

1)compareAndSet源码调用unsafe.compareAndSwapInt:

//unsafe这个类大部分方法都是native,调用底层C的代码。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

2)compareAndSwapInt在C++层面的源码:

在这里插入图片描述

其核心指令:Atomic::cmpxchg(x,addr,e),cmpxchg是compare和exchange缩写。

3)汇编实现:Atomic::cmpxchg(x,addr,e)
在这里插入图片描述

在汇编层面,在多线程中,比较与交换操作加了一把lock锁,锁住了比较与交换这两个操作,所以在这个过程中是原子性的。

4)多看一个指令,获取lock指令的方法:Lock_IF_MP

结论

因为CAS底层调用的汇编命令Atomic::cmpxchg,在多线程情况下,会加锁,所以确保了比较并交换操作的原子性。

ABA问题

  • 简单来说,ABA问题就是某个线程在其他线程还未进行CAS操作时,修改了变量A值=B,然后又修改B=A,然后其他线程开始CAS操作,比较原先的值=A,所以正常执行。但其他线程不知道此时的A已经被转换成了B,又换回了A,相当于不知道A是个二手的,还以为是新货。

  • 代码解释:

//以下假设有两个线程
Integer oldValue = 1;//线程1要修改oldValue=2
//此时线程2进来,将oldValue改值:1->0->1,
oldValue = 0;
oldValue = 1;
atomicInteger.compareAndSet(1,2);//线程1执行
//线程1不知道线程2改过这个值,因为这一条代码直接判断oldValue=1,
//是正确的,所以正常的执行oldValue=2.

如何解决?添加版本号:

  1. 为要修改的资源添加版本号,每修改一次,版本号+1,然后CAS操作时,校验这个版本号,版本号相同,才能进行CAS,否则失败。

  2. 为要修改的资源添加版本号,每修改一次,版本号+1,然后CAS操作时,校验这个版本号,版本号相同,才能进行CAS,否则失败。

  3. 例如上面的例子,为oldValue添加一个版本号version=1,当线程2修改1:(version=1)->0:(version=2)->1:(version=3)后,此时版本号应该version=3,而线程1执行CAS,就比较此时是否version=1,如果不等,那么不允许进行CAS操作。

更多文章可以光临我的博客:www.baidetu.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值