CAS三大问题

103 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

  • 如果 CAS 长时间一直不成功,会给 CPU 带来很大的开销,在Java的实现中是一直通过while循环自旋CAS获取锁。
  • 只能保证一个共享变量的原子操作
  • 引出了 ABA 问题

ABA问题

什么是ABA问题?

CAS引发的ABA问题:如下图 image.png

CAS 会导致 ABA 问题:

例: A、B线程从主存取出变量 value

-> A 在 N次计算中改变 value 的值
-> A 最终计算结果还原 value 最初的值
-> B 计算后,比较主存值与自身 value 值一致,修改成功

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

ABA问题的解决

image.png

思想很简单,可以很明显的看出来用版本号的方式解决了ABA的问题。

  • 除了对象值,AtomicStampedReference内部还维护了一个“状态戳”。
  • 状态戳可类比为时间戳,是一个整数值,每一次修改对象值的同时,也要修改状态戳,从而区分相同对象值的不同状态。
  • 当AtomicStampedReference设置对象值时,对象值以及状态戳都必须满足期望值,写入才会成功。

只能保证一个共享变量的原子操作

  • 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。还有一个方法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i=2,j=a合并一下ij=2a,然后用CAS来操作ij。从java1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。
  • 所以一般来说为了同时解决ABA问题和只能保证一个共享变量,原子类使用时大部分使用的是AtomicStampedReference