您的位置 首页 java

Java面试必考问题:如何解决CAS算法的ABA问题

前文《 》中介绍过CAS算法。JDK5增加 JUC包(java.util.concurrent) ,其中Atomic原子类都使用了CAS操作。这些CAS操作基于Unsafe类中的native方法实现。

在资源竞争少时,CAS基于硬件实现,不进入内核,不切换 线程 ,操作自旋几率小,相比 synchronized ,CAS有更高的性能。但CAS也有一些问题:

  • ABA问题
  • 自旋开销
  • 只能保证一个共享变量的 原子操作

CAS

如果CAS操作自旋长时间不成功会给CPU带来非常大的执行开销。

另外,当操作涉及多个共享变量时,CAS操作无效。这时我们可以考虑用AtomicReference引用类型来封装多个字段,来保证多变量操作的原子性。

ABA问题

ABA问题

上图展示了ABA问题的出现:变量 var 初次读时是 A 值,被赋值时也是 A 值,但期间变量被其他线程一度赋值成 B 值,后来又改回 A 值,CAS会误认为变量 var 从没被修改过。这样就语义就违背了真实情况。

那么如何解决ABA问题?

通过加入版本号或者其他标识,来识别变量是否被修改过,避免ABA问题

我们可以通过加一个版本号 version 来解决ABA问题。对于所有使用变量 var 的线程,如果修改了变量的值,都需要将对应的版本号 version 进行递增。CAS在进行变量前后值的对比时,除了要对比原来的变量值,还要对比版本号的值是否一样,这样就解决了ABA问题。

乐观锁的实现机制主要包括版本号机制(给数据加一个版本号,数据被修改版本号会加一,更新时读取版本号,若读取到的版本号和之前一致才更新,否则驳回)。

JUC包的解决方案

在JUC包中提供了 AtomicStampedReference AtomicMarckableReference 类,这两个类具有监测ABA问题的能力。其中的 compareAndSet() 方法首先检查当前对象值是否等于预期值,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将对象值和标志的值都更新为新值。

AtomicStampedReference 在构造方法中加入了类似时间戳( stamp )的字段作为标识,采用自增 int 作为 stamp ,在stamp不重复的前提下可以解决ABA问题, AtomicStampedReference 可以获知对象值被更改了几次。

如果我们不需要知道对象值被更改几次,而仅需要知道是否被更改过的话,那么可以使用 AtomicMarkableReference ,这个类用 boolean 变量 表示变量是否被更改过。

我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法, 欢迎大家关注 ,谢谢。

文章来源:智云一二三科技

文章标题:Java面试必考问题:如何解决CAS算法的ABA问题

文章地址:https://www.zhihuclub.com/199738.shtml

关于作者: 智云科技

热门文章

网站地图