Java 并发之CAS机制

CAS(Compare And Swap)是一种无锁编程机制,用于在并发环境中更新变量。它避免了锁的开销,但在高并发下可能导致CPU开销大。文章详细解释了CAS的工作原理,指出其不能保证多变量原子性操作的缺点,并深入讨论了ABA问题及其解决方案,即通过版本号机制防止ABA问题的发生。此外,还提到了在并发量高时,Synchronized可能优于CAS的选择。
摘要由CSDN通过智能技术生成

目录

 

一、什么是CAS机制

二、CAS的缺点

2.3 ABA问题

2.4 ABA问题的解决方法

三、CAS的应用场景


一、什么是CAS机制

CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。

CAS中使用了三个操作数:内存地址V,旧的预期值A,要修改的新值B

更新一个变量时,只有当 变量的内存地址V的值和旧的预期值A相同时,才会把变量更新成B。

可能有点抽象,下面举个简单例子:

1.在内存空间中存储着值为10的变量:

2.此时线程1想要把变量增加1,即对线程1来说,旧的预期值A=10,要修改的新值B=11

3.在线程1要提交更新之前,另一个线程2抢先把内存地址V中的变量修改成11。

4. 这时线程1开始提交更新,首先进行A和内存V值的比较(Compare),A=10,内存V=11,明显不同,所以此时线程1提交更新失败。

5. 线程1重新获取内存V的值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋

6. 这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A和地址V的实际值是相等的。

7. 线程1进行SWAP,把地址V的值替换为B,也就是12。

从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。

二、CAS的缺点

1.CPU开销大:在并发量很高的情况下,如果许多线程反复尝试更新一个变量,却一直更新不成功,循环往复会带给CPU很大压力。

2.不能保证代码块的原子性:CAS只能保证单个变量的原子性操作,但不能保证多个变量的原子性操作。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized

3.ABA问题

2.3 ABA问题

ABA问题,就是一个变量从A改成了B,又从B改成了A。

例如:

假设内存中有一个值为A的变量,存储在地址V中:

此时有三个线程想通过CAS更新这个变量的值,每个线程的执行时间有略微的偏差。线程1和线程2已经获得当前值,线程3还未获得当前值。

这时候线程1先工作,成功地把当前值从A改成B;同时线程2由于某些原因被阻塞住,没做更新操作,线程3在线程1更新之后,获得了当前值B。

再之后,线程2仍然处于阻塞状态,线程3继续执行,成功把当前值从B更新成了A。

最后,线程2终于恢复了运行状态,由于阻塞之前已经获得了“当前值”A,并且经过compare检测,内存地址V中的实际值也是A,所以成功把变量值A更新成了B。

这个过程中,线程2获取到的变量值A是一个旧值,尽管和当前的实际值相同,但内存地址V中的变量已经经历了A->B->A的改变。

这看上去好像也没什么问题,但是结合具体场景而言,就看出问题了。

如:

1.举一个提款机的例子。假设有一个遵循CAS原理的提款机,小灰有100元存款,要用这个提款机来提款50元。

2.由于提款机硬件出了点小问题,小灰的提款操作被同时提交两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。

3.理想情况下,应该一个线程更新成功,另一个线程更新失败,小灰的存款只被扣一次。

4.线程1首先执行成功,把余额从100改成50。线程2因为某种原因阻塞了。这时候,小灰的妈妈刚好给小灰汇款50元

5.线程2仍然是阻塞状态,线程3执行成功,把余额从50改成100。

6.线程2恢复运行,由于阻塞之前已经获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以成功把变量值100更新成了50。

那么问题就来了,相当于扣多了一次50块钱。

2.4 ABA问题的解决方法

给变量加版本号

在Compare阶段不仅要比较期望值A和地址V中的实际值,还要比较变量的版本号是否一致。

我们仍然以最初的例子来说明一下,假设地址V中存储着变量值A,当前版本号是01。线程1获得了当前值A和版本号01,想要更新为B,但是被阻塞了。

这时候,内存地址V中的变量发生了多次改变,版本号提升为03,但是变量值仍然是A。

随后线程1恢复运行,进行Compare操作。经过比较,线程1所获得的值和地址V的实际值都是A,但是版本号不相等,所以这一次更新失败。

这其实就是乐观锁的做法,跟Mysql的乐观锁思想一样。

三、CAS的应用场景

但是Synchronized和CAS没有说哪一个更好,在并发量很高的情况下,反而用Synchronized会好一点。因为CAS属于乐观锁,如果频繁有线程对共享资源进行写操作的话,那么乐观锁就会失败,会重复判断共享资源是否被改变过,会耗费很大cpu资源。

出处:

https://www.sohu.com/a/215510186_465221

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值