CAS中ABA问题及解决

一、ABA 场景描述

基于上节中提到的 CAS 原理,CAS 算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。设想一个场景:有线程 A、B 两个线程想对同一变量 Num = 1 进行修改,此时有:

第一步:线程 A 读取 Num 值为 1 。

第二步:线程 B 读取 Num 值为 1 。

第三步:线程 B 修改 Num 值为 2 。

第四步:线程 B 修改 Num 值为 1 。

第五步:线程 A 修改 Num 值为 2 。

以上场景可以看出,尽管线程 A 对 Num 的 CAS 操作进行成功了,但这不代表这个过程中没有问题。在没有垃圾回收机制的内存模型中(如C++),内存可随意释放:有线程 A、B 两个线程,

第一步:线程 A 从内存位置 V 中取出 M ,M 指向内存位置 W。

第二步:线程 B 从位置 V 中取出 M。

第三步:线程 B 进行了一些操作,释放了 M 指向的内存。

第四步:线程 B 重新申请内存,并恰好申请了内存位置 W,将位置 W 存入 N 的内容。

第五步:线程 B 将内存位置 W 写入位置 V。

第六步:线程 A 进行 CAS 操作,发现位置V中仍然是 M 指向的即内存位置 W,操作成功。

上述场景二要比场景一的问题更大,线程 A 与 线程 B 根本无法感知到 M 的内容已被修改,甚至如果线程 B 只释放内存,则 M 最后只会变成一个野指针。

二、AtomicStampedReference

Java 的垃圾回收机制已经帮我们解决了场景二,而场景一的解决加入版本号即可。

除了对象值,AtomicStampedReference 内部还维护了一个状态戳。状态戳可类比为时间戳,是一个整数值,每一次修改对象值的同时,也要修改状态戳,从而区分相同对象值的不同状态。当AtomicStampedReference 设置对象值时,对象值以及状态戳都必须满足期望值,写入才会成功。AtomicStampedReference 的几个 API 在 AtomicReference 的基础上新增了有关时间戳的信息:

//构造方法, 传入引用和戳
public AtomicStampedReference(V initialRef, int initialStamp)
//返回引用
public V getReference()
//返回版本戳
public int getStamp()
//如果当前引用 等于 预期值并且 当前版本戳等于预期版本戳, 将更新新的引用和新的版本戳到内存
public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)
//如果当前引用 等于 预期引用, 将更新新的版本戳到内存
public boolean attemptStamp(V expectedReference, int newStamp)
//设置当前引用的新引用和版本戳
public void set(V newReference, int newStamp) 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值