尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【面试题-多线程】什么是 ABA 问题,怎么产生的,怎么解决?

【面试题-多线程】什么是 ABA 问题,怎么产生的,怎么解决?
📅 发布时间:2026/7/30 7:37:37

一篇吃透CAS的ABA问题:成因、风险与全套解决方案

前言💡

并发编程中我们经常使用CAS(Compare And Swap)实现无锁并发,相比重量级synchronized拥有更高吞吐量。很多开发者熟练使用CAS,却忽略它经典的ABA漏洞。
本文循序渐进讲清楚:

  1. 什么是ABA问题
  2. ABA如何产生、会造成什么事故
  3. 主流解决方案原理 + 代码示例
  4. 方案优缺点横向对比

一、什么是CAS?快速回顾📚

CAS全称比较并交换,是乐观锁底层原子操作,操作分为两步:

  1. 比较:判断内存当前值 == 预期旧值
  2. 交换:相等则更新为新值;不相等直接失败

伪代码逻辑:

if(内存值==预期旧值){内存值=新值;returntrue;}else{returnfalse;}

注意:原生CAS只对比数值本身,这就是ABA问题的根源!

二、什么是ABA问题?🤔

ABA定义:线程1读取变量值为A,在线程1执行CAS前;变量被其他线程修改为B,随后又被改回A。线程1执行CAS时发现值依旧是A,认为变量没有变动,执行更新,最终引发数据异常。

简单一句话:值看上去没变,但中间被篡改过!

ABA完整时序流程图

内存变量 V=A线程2🟥线程1🟦内存变量 V=A线程2🟥线程1🟦线程1阻塞/失去CPU时间片⏸️值变回A,肉眼看不出变化CAS判定成功,执行覆盖更新!⚠️读取V,获取值A(准备执行CAS)CAS(A→B),修改成功 V=BCAS(B→A),修改成功 V=A恢复运行,执行CAS(A,新值)

三、ABA问题真实场景举例🧨

最经典场景:并发栈无锁操作(单向链表实现栈)
栈结构:A → B → C,栈顶 = A

  1. 线程T1准备弹出栈顶A,读取头结点A;准备执行CAS
  2. T1暂停;线程T2执行出栈:弹出A,栈变成B→C
  3. T2继续把A入栈,栈变回A→B→C
  4. T1恢复执行CAS,发现栈顶依旧是A,执行出栈
    最终链表指针错乱,出现内存丢失、循环链表、数据泄露严重bug!

风险总结

原生CAS只校验数值,无法识别中间变更历史。
简单类型(数字)场景ABA危害不直观;链表、对象引用等基于指针的结构,ABA极易引发灾难性BUG。

四、ABA问题核心产生原因✅

  1. 基础版本CAS仅比较数据值,不记录变量修改历史;
  2. 变量经历:A → B → A,最终数值复原;
  3. 线程无法区分:【从未修改的A】 vs 【被修改后恢复的A】;

核心痛点:缺少版本标记。

五、ABA问题主流解决方案🛠️

方案1:带版本号的CAS(乐观锁版本号机制)

原理

不再只对比数据本身,每条数据附带一个自增version版本号:

  • 每次成功修改变量,版本号version = version + 1
  • CAS条件:旧数据 + 旧版本号 同时匹配,才能更新

时序图:

内存 V=A,version=1线程2🟥线程1🟦内存 V=A,version=1线程2🟥线程1🟦线程1暂停⏸️当前version=3≠1 → CAS失败 ❌获取 V=A,version=1CAS(A,1→B,2) ✔️ V=B,version=2CAS(B,2→A,3) ✔️ V=A,version=3CAS(A,1,新值)

Java中对应实现:AtomicStampedReference(携带戳stamp,等价版本号)

// 初始化 初始值A,版本戳stamp=1AtomicStampedReference<String>atomicRef=newAtomicStampedReference<>("A",1);intoldStamp=atomicRef.getStamp();StringoldVal=atomicRef.getReference();// 其他线程修改A→B→A,stamp自增为3// CAS:值A,版本1,期望匹配booleansuccess=atomicRef.compareAndSet("A","NEW",oldStamp,oldStamp+1);// 版本不匹配,返回false,成功规避ABA

方案2:AtomicMarkableReference(布尔标记)

存储一个boolean mark标记,只能区分「是否被修改过」,无法记录修改次数。
局限性:多次修改后标记会反复翻转,不能彻底杜绝ABA,适用场景有限。

方案3:数据库乐观锁 version字段

业务开发最常用!数据表增加version字段

-- 更新时带上版本条件UPDATEuserSETbalance=balance-100,version=version+1WHEREid=1ANDversion=#{oldVersion};

方案4:使用重量级锁(兜底方案)

直接抛弃无锁CAS,使用synchronized/ReentrantLock,同一时间只允许一个线程操作,从根源杜绝并发竞争。
缺点:并发性能下降,失去无锁优势。

方案5:使用GC+不变对象(不推荐)

每次修改新建对象,不复用旧对象引用,成本极高,极少使用。

六、方案优劣横向对比📊

方案能否彻底解决ABA性能适用场景
AtomicStampedReference(版本戳)✅ 完全解决高Java内存无锁并发
AtomicMarkableReference❌ 不完全解决高只关心是否改动,不关心次数
数据库version乐观锁✅ 完全解决中等数据库业务并发更新
synchronized 互斥锁✅ 完全解决较低简单并发,追求稳定不追求极致吞吐

七、常见误区澄清⚠️

  1. AtomicInteger / AtomicLong 会遇到ABA吗?
    会!基础原子类只有数值CAS,没有版本戳。
    但普通数值加减场景ABA很难触发严重故障;链表、队列无锁实现必须规避。

  2. 只要用CAS就一定要处理ABA?
    不是。如果业务允许「中间被修改又复原」这种场景,可以不用处理。
    但开发通用无锁数据结构(队列、栈)必须防范。

  3. stamp版本号会溢出吗?
    long型版本戳溢出周期极长,工程上基本不用考虑溢出问题。

八、总结📝

  1. ABA本质:变量先A→B→A,原生CAS只对比数值,无法感知中间变更;
  2. 最大风险:链表、无锁队列等基于引用操作的数据结构;
  3. 标准根治方案:增加自增版本号,使用带版本校验的CAS;Java优先使用AtomicStampedReference;数据库增加version乐观锁;
  4. 简单并发场景,也可以直接使用互斥锁规避并发竞争。

日常业务代码很少手写无锁链表,ABA问题遇到概率不高;但学习并发、阅读JDK源码(Concurrent相关容器)必须理解这个经典并发缺陷。

相关新闻

  • 重测信度:测量工具稳定性的核心评估方法与工程实践
  • 易潮解晶体加工注意事项
  • 磁吸易装+稳定传输,赋能机房U位资产高效智能管理

最新新闻

  • Android开发转型指南:从XML到Jetpack Compose的实战进阶
  • B站视频转文字终极指南:开源神器bili2text完整教程
  • 2026年如何甄选达标排放的废气焚烧稳定运行系统技术方案? - geo交流
  • 2026年茂名耐用的EL检测仪供应商怎么选?这份优选指南帮你避开选型雷区 - geo交流
  • AD2s1205旋变解码芯片实战:从硬件设计到软件配置全解析
  • gif压缩免费:投稿系统卡体积时,手机与电脑怎么对照压 - AI测评专家

日新闻

  • 终极TeamSpeak3音乐机器人搭建指南:5分钟实现语音聊天室音频播放
  • 广州海珠区内搬家攻略,平价靠谱搬家服务商推荐,专业打包搬运省心避坑全流程指南 - 厚道搬家
  • 大语言模型入门指南:从零到精通掌握AI核心技术的5大步骤

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号