乐观锁的CAS算法与版本号机制

乐观锁的实现基于两部分,一是CAS算法,二是版本号控制,这篇就聊一聊这两部分。

说一下悲观锁,认为所有线程都会对数据进行修改,所以某个线程拿数据的时候,就会上锁阻塞其余所有线程,直到锁的释放,然后其余线程继续竞争这个锁,往复循环。行锁、表锁、读锁、写锁都是悲观锁。

乐观锁,总是认为最好的情况,每次去拿数据都认为别人不会修改,所以不会上锁,但是在更新的时候会判断在此期间别人有没有去更新这个数据,实现技术就是CAS算法和版本号机制。

CAS思想

先比较内存C中的与寄存器A中的值(旧预期值)是否相等,如果相等,则寄存器B中的值(新值)写入内存;如果不相等,则不做任何操作。整个过程为原子性,不会被打断。

CAS操作分为三部分:

1、读取内存值。

2、比较内存值与期望值是否相等。

3、如果相等,新值写入内存,否则此次操作无效。

同时,CAS不会有内存可见性问题,CAS本身就是指令级别读取内存操作,编译器把一系列指令进行调整,把独写内存的指令调整成读取寄存器的指令,所以不会有内存可见性带来的线程不安全问题。

因此,CAS可以做到不加锁也能保证线程安全。

对Integer做i++原子性操作的AtomicInteger类的getAndIncrement方法并没有用到synchronized锁,这里我下载了JDK的源码,不然进不去最底层,一直进入这个方法的最里面:

可以看到,在运算过程中,执行了一个compareAndSwapInt方法,翻译为比较并交换,不过由于这个方法是native修饰的,由C实现,我们追不到最底层去分析,在这里,其实还存在一个版本号机制,主要用来防止ABA问题。

CAS的ABA问题

CAS的ABA问题是使用CAS时会遇到的问题,我们知道,CAS在内存值改变之前会先比较内存值和寄存器旧值是否相等,通过这个比较判断内存中的值是否发生改变。但是!万一对比的时候是相同的,但在新值给到内存值之前,内存中的值发生过一次改变,那这个时候就会出现问题。

CAS只能对比值是否相同,但不能确定这个值是否中间发生过改变。

打个比方,我们买一双鞋,但无法判断是刚仓库里的新鞋,还是别人摆到店里被无数人试穿的鞋。

ABA导致的问题

如果业务对于过程并不是那么的看重,那ABA也称不上是什么问题,但如果关注过程,那就会出现问题。
以银行存取款为例,假设小刚有200元存款,他想从银行取100元钱,银行创建了两个线程,并发执行扣款100元,我们希望线程1执行扣款成功,线程2扣款失败,但如果使用CAS处理这个扣款过程,就有可能出现问题。

  • 预期流程

    1、存款200。线程1获取到存款值200,期望更新值100;线程2获取到存款值200,期望更新值100。
    2、线程1开始执行,线程2阻塞等待线程1执行完毕,线程1执行完毕,存款值改为100。
    3、线程2开始执行,发现存款值为100,与之前读到的200不同,执行失败。
     
  • 非正常(异常)流程
    1、存款 200。线程1获取到存款值为 200,期望值更新为 100;线程2 获取到存款值为 200,期望值更新为 100。
    2、线程1开始执行,线程2 阻塞等待中,线程1扣款成功,存款被改成 100。
    3、在线程2执行前,小刚爸爸给小刚转账了 100,此时小刚账户余额又变成了 200!
    4、轮到线程2执行,发现当前存款为 200, 和之前读到的200相同, 再次执行扣款操作。

    这个时候,扣款操作进行了两次,这就是ABA引发的错误。

版本号机制

为了解决CAS的ABA问题,引出了版本号机制,在数据表中加上一个version版本号的字段,表示数据被修改的次数,这个值只能增加,不能减小,当某个线程要更新数据值时,在读取数据的同时也会读取version值,若读取的version值为当前数据库中的version值相等时,才会更新数据值,否则认为这个数据已被修改,则不做任何操作。

一般来说,乐观锁都是由CAS和版本号联用,版本号的存在是保证过程准确要素。

  • 43
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逻辑怪c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值