经典的ABA问题与解决方法

2023-11-02

1:AbA问题的产生

    要了解什么是ABA问题,首先我们来通俗的看一下这个例子,一家火锅店为了生意推出了一个特别活动,凡是在五一期间的老用户凡是卡里余额小于20的,赠送10元,但是这种活动没人只可享受一次。然后火锅店的后台程序员小王开始工作了,很简单就用cas技术,先去用户卡里的余额,然后包装成AtomicInteger,写一个判断,开启10个线程,然后判断小于20的,一律加20,然后就很开心的交差了。可是过了一段时间,发现账面亏损的厉害,老板起先的预支是2000块,因为店里的会员总共也就100多个,就算每人都符合条件,最多也就2000啊,怎么预支了这么多。小王一下就懵逼了,赶紧debug,tail -f一下日志,这不看不知道,一看吓一跳,有个客户被充值了10次!

阐述:

假设有个线程A去判断账户里的钱此时是15,满足条件,直接+20,这时候卡里余额是35.但是此时不巧,正好在连锁店里,这个客人正在消费,又消费了20,此时卡里余额又为15,线程B去执行扫描账户的时候,发现它又小于20,又用过cas给它加了20,这样的话就相当于加了两次,这样循环往复肯定把老板的钱就坑没了!

本质:

ABA问题的根本在于cas在修改变量的时候,无法记录变量的状态,比如修改的次数,否修改过这个变量。这样就很容易在一个线程将A修改成B时,另一个线程又会把B修改成A,造成casd多次执行的问题。

 

2:AtomicStampReference 

AtomicStampReference在cas的基础上增加了一个标记stamp,使用这个标记可以用来觉察数据是否发生变化,给数据带上了一种实效性的检验。它有以下几个参数:

//参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值
public boolean compareAndSet(V expected,V newReference,int expectedStamp,int newStamp);

public V getRerference();

public int getStamp();

public void set(V newReference,int newStamp);

 

3:AtomicStampReference的使用实例

我们定义了一个money值为19,然后使用了stamp这个标记,这样每次当cas执行成功的时候都会给原来的标记值+1。而后来的线程来执行的时候就因为stamp不符合条件而使cas无法成功,这就保证了每次

只会被执行一次。

public class AtomicStampReferenceDemo {

    static AtomicStampedReference<Integer>  money =new AtomicStampedReference<Integer>(19,0);

    public static void main(String[] args) {

        for (int i = 0; i < 3; i++) {

            int stamp = money.getStamp();

            System.out.println("stamp的值是"+stamp);

            new Thread(){         //充值线程

                @Override
                public void run() {

                        while (true){

                            Integer account = money.getReference();

                            if (account<20){

                                if (money.compareAndSet(account,account+20,stamp,stamp+1)){

                                    System.out.println("余额小于20元,充值成功,目前余额:"+money.getReference()+"元");
                                    break;
                                }
                            }else {

                                System.out.println("余额大于20元,无需充值");
                            }
                        }
                    }
                }.start();
            }


            new Thread(){

                @Override
                public void run() {    //消费线程

                    for (int j = 0; j < 100; j++) {

                        while (true){

                            int timeStamp = money.getStamp();//1

                            int currentMoney =money.getReference();//39

                            if (currentMoney>10){
                                System.out.println("当前账户余额大于10元");
                                if (money.compareAndSet(currentMoney,currentMoney-10,timeStamp,timeStamp+1)){

                                    System.out.println("消费者成功消费10元,余额"+money.getReference());

                                    break;
                                }
                            }else {
                                System.out.println("没有足够的金额");

                                break;
                            }
                            try {
                                Thread.sleep(1000);
                            }catch (Exception ex){
                                ex.printStackTrace();
                                break;
                            }

                        }

                    }
                }
            }.start();

        }
    }

 

 这样实现了线程去充值和消费,通过stamp这个标记属性来记录cas每次设置值的操作,而下一次再cas操作时,由于期望的stamp与现有的stamp不一样,因此就会设值失败,从而杜绝了ABA问题的复现。

 

摘自:https://www.cnblogs.com/wyq178/p/8965615.html 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

经典的ABA问题与解决方法 的相关文章

随机推荐

  • 1929:【04NOIP普及组】火星人

    题干 这道题有好多废话 不过和全排列非常像 全排列题目 所以这道题数字的大小顺序与全排列的默认顺序一模一样 全排列的代码 在这里 本题就是一次次地调用全排列 不愿意麻烦的 就是我 可以用STL 非常方便 代码 100分 include
  • Stream:findFirst()高效简洁遍历集合中的一个元素

    业务开发中会运用到很多的List
  • sonarqube项目按权限分配

    一 创建用户 进入sornarqube的配置中心 选择权限菜单下的用户菜单 然后点击Create User按钮 只要输入登录名 用户名与密码保存后就可以创建一个新用户 二 创建组 选择权限下的群组菜单进入用户组管理页面 点击Create G
  • 时序数据交叉验证方法与python实现

    文章目录 传统N折交叉验证方法 时序数据交叉验证方法 方法1 窗口拆分 方法2 带间隔的窗口拆分 方法3 拓展窗口切分 时序交叉验证python复现 传统N折交叉验证方法 传统的N折交叉验证示意图如下图所示 时序数据交叉验证方法 由于时间序
  • 谨慎对待Go语言中对interface的nil判断

    谨慎对待Go语言中对interface的nil判断 在进行Go语言编程中 我们会看见诸如if err nil 或者if err nil 之类的判断 这跟go语言的错误处理哲学 计划失败而非成功 及早失败 有关 大多数情况下 我们对一个err
  • StringRedisTemplate运行NullPointerException的完全解决

    SpringBoot在使用Redis时出现StringRedisTemplate运行NullPointerException的完全解决 RedisTemplate运行NullPointerException的完全解决 三种解决方法 第一种
  • STM之SD卡

    SD卡基本函数 SPI InitTypeDef结构体 SPI 初始化结构体 1 SD Lowlevel Init 该函数初始化SPI相关外围时钟 配置GPIO引脚 调用SPI Init 初始化SPI InitStructre结构体 gt S
  • Linux下如何安装Anaconda、修改环境变量以及管理环境

    Linux下AI环境的搭建 Anaconda篇 1 第一步 安装anaconda 2 第二步 更改 调整系统环境PATH 3 第三步 创建适当的虚拟 python pip 工作区 3 第四步 如何使用conda来管理不同环境 1 第一步 安
  • java获取客户端操作系统_根据User-Agent,获得客户端浏览器和操作系统的信息

    碰到取客户端浏览器和操作系统信息的问题 网上找了很久 大多都是在页面中嵌入JS实现的 无法满足我的要求 所以 就自己写了个方法 可以在servlet中取到 我是根据每个浏览器User Agent的特征 来判断的 在windows xp中已测
  • 汇编实现排序——希尔排序

    希尔排序是把记录按下标的一定增量分组 对每组使用直接插入排序算法排序 随着增量逐渐减少 每组包含的关键词越来越多 当增量减至1时 整个文件恰被分成一组 算法便终止 以下是汇编实现的代码 S0 SEGMENT STACK DW 20 DUP
  • RabbitMQ学习(四)——高级特性

    RabbitMQ高级特性 1 1 消息的可靠投递 1 1 1 消息发送端 在使用RabbitMQ的时候 作为消息发送方式希望杜绝任何消息丢失或者投递失败的场景 RabbitMQ为我们提供了两种方式用来控制消息的投递可靠性模式 confirm
  • IDEA断点调试技巧,多张动图包教包会。

    文章目录 一 怎么开启断点调试 二 调试界面咋那么多按钮 1 返回断点位置 2 步过 3 步入 4 5 强制步入 步出 6 回退断点 7 断点跳到光标处 8 表达式计算 9 恢复程序 10 停止程序 11 查看所有断点 12 禁用断点 13
  • vs2017community进行DirectX开发_问题小节

    一 下载并安装vs及DirectX 1 vs版本 vs2017community 免费的 百度就有 2 我下载的DirectX版本 DXSDK Jun10 链接https pan baidu com s 1w7np5N E6QYqJXIr0
  • 微信小程序年月日时分选择器的实现

  • 基于VGG深度学习神经网络的猫狗数据集分类

    摘要 VGG网络是由牛津大学视觉几何组完成的基于深度卷积神经网络的大规模图像识别架构 该网络参考了AlexNet ZFNet OverFeat等经典的网络架构 从而得出的 这个架构参加了ILSVRC 2014比赛 取得了定位冠军 分类亚军的
  • 苹果App Store程序提交审核指南中文版

    注 lt 苹果应用商店审核指南 gt 中文翻译最近一次更新为2013 03 04 文中红色部分是相对于2013 03 04版本的新增内容 绿色部分代表更改的内容 蓝色表示苹果相关官方文档的链接 前言 感谢您付出宝贵的才华与时间来开发iOS应
  • cannot import name ‘__new_empty_tensor‘ 升级版本后依旧出现问题

    问题描述 原因说明 这是因为包版本过低导致的 升级到torchvision 0 9 0就没有这个问题了 后续问题 我发现更新了版本依旧出现 后面发现因为我是多版本Python 运行的时候使用了默认环境 更新包在另一个环境 所有出现了问题 解
  • 含源码,用Python实现浪漫烟花

    目录 前言 环境准备 代码编写 效果展示 前言 Python实现浪漫的烟花特效 现在很多地方都不能放烟花了 既然看不到 那作为程序猿的我们还不能自己用代码做一个吗 今天就带大家用代码做一个烟花特效吧 环境准备 这里使用到的库有 pygame
  • Spring 启动详解

    Springboot启动流程 方法入口 org springframework boot SpringApplication run java lang String StopWatch 开启 公开总运行时间和每个命名任务的运行时间 get
  • 经典的ABA问题与解决方法

    1 AbA问题的产生 要了解什么是ABA问题 首先我们来通俗的看一下这个例子 一家火锅店为了生意推出了一个特别活动 凡是在五一期间的老用户凡是卡里余额小于20的 赠送10元 但是这种活动没人只可享受一次 然后火锅店的后台程序员小王开始工作了