写点什么

CAS 之 ABA 问题的解决方法,深入理解 Nginx 及使用 Nginx 实现负载均衡

作者:Java高工P7
  • 2021 年 11 月 09 日
  • 本文字数:1652 字

    阅读完需:约 5 分钟

System.out.println("res2=" + res2);


}).start();


new Thread(() -> {


boolean res3 = atomicReference.compareAndSet(100, 200);


System.out.println("res3=" + res3);


}).start();


}


}


运行结果:res1,res2,res3 都是 true,这说明虽然线程 t1 修改了 atomicReference 中的值,但是由于修改后值与原来的一样,所以线程 t2 在判断的时候认为该值没有被修改过 CAS 操作成功。


AtomicStampedReference 解决 ABA 问题




为了解决 ABA 问题,引入了 AtomicStampedReference,AtomicStampedReference 它内部不仅维护了对象值,还维护了一个时间戳(版本号)。当 AtomicStampedReference 对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。

AtomicStampedReference 的构造器

在初始化的时候传入两个参数,初始化的引用值和版本号


public AtomicStampedReference(V initialRef, int initialStamp) {


pair = Pair.of(initialRef, initialStamp);


}

AtomicStampedReference 的 compareAndSet 方法

public boolean compareAndSet(V expectedReference,


V newReference,


int expectedStamp,


int newStamp) {


Pair<V> current = pair;


return


expectedReference == current.reference &&


expectedStamp == current.stamp &&


((newReference == current.reference &&


newStamp == current.stamp) ||


casPair(current, Pair.of(newReference, newStamp)));


}


  • expectedReference : 期望值

  • newReference


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


: 想要更新成的新的值


  • expectedStamp : 期望的版本号

  • newStamp : CAS 操作成功要更新成的版本号


然后每次操作的时候都会先比较版本号,版本号一致才能操作成功,每次操作成功后都将版本号增加+1(版本号只加不减)

测试代码

import java.util.concurrent.TimeUnit;


import java.util.concurrent.atomic.AtomicReference;


import java.util.concurrent.atomic.AtomicStampedReference;


public class ABASolve {


public static void main(String[] args) {


AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);


int stamp = atomicStampedReference.getStamp();


new Thread(() -> {


System.out.println("t1 线程拿到的初始版本号:" + stamp);


System.out.println("t1 线程拿到的初始值:" + atomicStampedReference.getReference());


//


boolean res1 = atomicStampedReference.compareAndSet(100, 101, stamp, atomicStampedReference.getStamp() + 1);


System.out.println("修改结果:" + res1);


System.out.println("t1 线程修改之后的版本号:" + atomicStampedReference.getStamp());


System.out.println("t1 线程修改之后的值:" + atomicStampedReference.getReference());


boolean res2 = atomicStampedReference.compareAndSet(101, 100, 2, atomicStampedReference.getStamp() + 1);


System.out.println("修改结果:" + res2);


}).start();


new Thread(() -> {


System.out.println("t2 线程拿到的初始版本号:" + stamp);


try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }


boolean b1 = atomicStampedReference.compareAndSet(100, 101, stamp, atomicStampedReference.getStamp() + 1);


System.out.println("t2 线程期望的版本号为" + stamp + ",t2 线程当前查看主内存中 atomicStampedReference 真实的版本号为:" + atomicStampedReference.getStamp());


System.out.println("t2 线程 CAS 操作的结果为:" + b1);


}).start();


}


}


该代码模拟了线程 1 将 atomicStampedReference 值修改后又改回成原来的值的过程,观察版本号的变化以及最后线程 2 的 CAS 操作成功与否,运行结果如下:


t1 线程拿到的初始版本号:1


t1 线程拿到的初始值:100


修改结果:true


t1 线程修改之后的版本号:2


t1 线程修改之后的值:101


修改结果:true


t2 线程拿到的初始版本号:1


t2 线程期望的版本号为 1,t2 线程当前查看主内存中 atomicStampedReference 真实的版本号为:3


t2 线程 CAS 操作的结果为:false

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
CAS之ABA问题的解决方法,深入理解Nginx及使用Nginx实现负载均衡