还在盲信无锁编程?你知道 CAS 机制背后的 ABA 黑洞能吞掉你多少年终奖吗?
全文目录开篇语0. 前言那该死的“并发”噩梦1. CAS 到底是何方神圣别背书听人话2. ABA 问题当你以为前女友没谈恋爱... 真实惨案现场链表版3. 解决方案给你的爱加上“时间戳”️ 实战代码AtomicStampedReference4. 深度拓展除了 Java咱们还能在哪里秀数据库领域的“乐观锁”自旋的代价别光看好的5. 写在最后别做“全栈”要做“全懂”文末开篇语哈喽各位小伙伴们你们好呀我是喵手。运营社区C站/掘金/腾讯云/阿里云/华为云/51CTO欢迎大家常来逛逛今天我要给大家分享一些自己日常学习到的一些知识点并以文字的形式跟大家一起交流互相学习一个人虽可以走的更快但一群人可以走的更远。我是一名后端开发爱好者工作日常接触到最多的就是Java语言啦所以我都尽量抽业余时间把自己所学到所会的通过文章的形式进行输出希望以这种方式帮助到更多的初学者或者想入门的小伙伴们同时也能对自己的技术进行沉淀加以复盘查缺补漏。小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持0. 前言那该死的“并发”噩梦兄弟们咱们干后端的谁没半夜被并发 Bug 吓醒过 尤其是当你发现数据库里的钱数不对或者库存莫名其妙超卖的时候那种后背发凉的感觉简直比相亲遇到前女友还尴尬。为了追求极致的性能KPI我们把synchronized这种重型锁扔进了垃圾桶拥抱了传说中的“无锁编程”。于是CASCompare And Swap成了我们的救世主。大家都说它快说它好说它是并发界的“瑞士军刀”。但是Too young, too simple! 你以为 CAS 就是银弹哼它要是疯起来连它自己都怕特别是那个阴魂不散的ABA 问题它就像是那种看着老实巴交、实则一肚子坏水的“隐形杀手”。今天老哥我就带你扒开 CAS 的外衣看看它到底有多性感又有多危险1. CAS 到底是何方神圣别背书听人话咱们先不整那些晦涩的术语。CAS全称 Compare And Swap比较并交换。听着挺高大上其实说白了它就是个死心眼的倔驴。想象一下你要去抢购一台 iPhone 16别问为啥不是 15咱们要有前瞻性。传统的synchronized锁就像是商场只有一个柜台你进去了把门反锁慢悠悠地买外面几千人在风中凌乱排队。安全是安全但效率低到令人发指而CAS是怎么玩的呢它不锁门。它就像是你带着两个小纸条冲到柜台前纸条 V (Value)柜台里现在的库存内存里的实际值。纸条 A (Expect)你以为的库存旧的预期值。纸条 B (New)你买完后剩下的库存想修改的新值。CAS 的逻辑是这样的“嘿柜员我看一眼Compare如果现在的库存 V 等于我预期的 A说明没人插队抢货那你赶紧把库存改成 BSwap如果 V 不等于 A说明哪个孙子刚才手快抢走了那我就不算数了我重试自旋或者认怂”看懂没这就叫乐观锁它总是假设“没人跟我抢”真有人抢了再哭着重来。我们来看一眼 Java 里AtomicInteger的底层实现这玩意儿就是 CAS 的亲儿子// Java Implementation of AtomicInteger (Simplified)publicfinalintgetAndIncrement(){// 这里的 this 是当前对象valueOffset 是内存地址偏移量1 是要加的数// 这是一个死循环自旋直到成功为止for(;;){intcurrentget();// 拿到当前的 Aintnextcurrent1;// 计算出想要的 B//以此为誓若内存里的值还是 current就给老子换成 nextif(compareAndSet(current,next))returncurrent;}}这一波操作直达 CPU 汇编指令cmpxchg这是硬件级别的原子性支持这就叫专业2. ABA 问题当你以为前女友没谈恋爱…这才是今天的重头戏CAS 看起来很完美对吧效率高不用上下文切换。但是它有一个致命的逻辑漏洞就是 ABA 问题。啥是 ABA听我给你讲个狗血的故事你桌上放了一杯水状态 A。你去上了个厕所。这时候你那个损友过来把水喝干了状态 B然后觉得心里过意不去又接了一杯一模一样的水放回去状态 A。你上完厕所回来看了一眼“哟水还在A A没人动过”于是你端起来一口闷了。这就是 ABA 问题仅仅比较值是不是相等是无法判断这个过程是否被“偷梁换柱”过的你可能会说“喝杯水怎么了又不死人。”嘿嘿如果在链表Stack操作里这能让你程序直接原地爆炸 真实惨案现场链表版假设咱们有个无锁栈Stack里面有两个节点Top - A - B。线程 1想要把 A 弹出来Pop它的期望是CAS(Top, A, B)。也就是如果 Top 还是 A就把 Top 指向 B。就在线程 1 准备执行 CAS 还没执行的那一瞬间线程 1 挂起了被系统调度走了。⏸️这时候线程 2也就是那个捣乱的损友冲进来了它把 A 弹出来了。它把 B 也弹出来了。它又把 A 压回去了现在的栈结构是Top - A注意B 已经没了A 后面可能是 null 或者垃圾数据。线程 1 醒了它看了一眼 Top“哇还是 A 耶看来一切正常”于是它自信地执行了 CAS把 Top 指向了 B。可是 B 早就被线程 2 删掉了啊B 现在的内存地址可能已经是悬空的或者被重用成了别的东西这就导致了传说中的野指针或者数据错乱。你的程序就像脱缰的野马奔着内存泄漏和崩溃就去了。这就问你怕不怕3. 解决方案给你的爱加上“时间戳”既然单纯比对“值”不可靠那咱们就得加码就像给猪肉盖章一样咱们得给数据盖个版本号Version。这就是解决 ABA 问题的核心思路版本号机制。原本是A - B - ACAS 觉得 OK现在变成1A - 2B - 3A这时候 CAS 一看“卧槽虽然值还是 A但版本号从 1 变成 3 了这明显被人动过手脚啊” 拒绝执行️ 实战代码AtomicStampedReference在 Java 里JDK 的大佬们早就给咱们准备好了工具AtomicStampedReference。这名字听着就贵气自带“邮戳Stamp”。来咱们看段代码这可是能直接跑的干货别眨眼importjava.util.concurrent.atomic.AtomicStampedReference;publicclassABASolutionDemo{// 初始值是 100初始版本号是 1staticAtomicStampedReferenceIntegeratomicStampedRefnewAtomicStampedReference(100,1);publicstaticvoidmain(String[]args){// 线程 1那是那个捣乱的损友模拟 ABA 操作newThread(()-{intstampatomicStampedRef.getStamp();System.out.println(Thread 1 - Initial Stamp: stamp);try{Thread.sleep(100);}catch(InterruptedExceptione){}// 第一次修改100 - 101atomicStampedRef.compareAndSet(100,101,atomicStampedRef.getStamp(),atomicStampedRef.getStamp()1);// 第二次修改101 - 100 (这就构成了 ABA!)atomicStampedRef.compareAndSet(101,100,atomicStampedRef.getStamp(),atomicStampedRef.getStamp()1);System.out.println(Thread 1 - ABA operation done. Current Stamp: atomicStampedRef.getStamp());}).start();// 线程 2这是蒙在鼓里的你newThread(()-{intstampatomicStampedRef.getStamp();// 刚开始拿到的版本号是 1intreferenceatomicStampedRef.getReference();// 刚开始拿到的值是 100System.out.println(Thread 2 - Initial Stamp: stamp);// 故意睡得比线程 1 久让线程 1 有足够时间搞事情try{Thread.sleep(200);}catch(InterruptedExceptione){}// 此时内存里的值虽然是 100但版本号已经是 3 了// 咱们手里拿的版本号还是 1所以这里 CAS 绝对会失败booleanresultatomicStampedRef.compareAndSet(reference,// 期望值 1002024,// 新值 2024stamp,// 期望版本号 1stamp1// 新版本号 2);System.out.println(Thread 2 - Update Success? result);// 结果必然是 false这就是版本号的威力️if(!result){System.out.println(Thread 2: Damn! Someone touched my data! );System.out.println(Current actual stamp is: atomicStampedRef.getStamp());}}).start();}}看到了吗虽然值回到了 100但因为 stamp 对不上线程 2 的操作被拦下来了避免了可能发生的逻辑错误。这就是技术的魅力啊兄弟们✨4. 深度拓展除了 Java咱们还能在哪里秀别以为 CAS 只是 Java 的面试题这思想在计算机科学里到处都是数据库领域的“乐观锁”你在写 SQL 的时候是不是经常处理库存扣减如果你直接UPDATE goods SET stock stock - 1 WHERE id 1并发高了数据库容易死锁。聪明人都怎么干加个version字段UPDATEgoodsSETstockstock-1,versionversion1WHEREid1ANDversionold_version;这就是数据库层面的 CAS ABA 解决方案如果返回的影响行数是 0说明被别人插队了报错给前端或者重试。这就把压力从数据库锁转移到了业务逻辑上高并发系统的基石啊️自旋的代价别光看好的虽然 CAS 好但它有个坏毛病自旋Spin。如果并发冲突太厉害比如几千个线程同时抢一个变量大部分线程都会处于while(true)的死循环里空转。这时候你的 CPU 占用率会直接飙到 100%风扇转得跟直升机一样响所以CAS 适合“读多写少”或者“冲突概率低”的场景。要是冲突高老老实实加synchronized或者ReentrantLock吧别硬撑硬撑容易过劳肥。5. 写在最后别做“全栈”要做“全懂”咱聊了这么多其实核心就一句话没有什么技术是完美的只有最适合场景的技术。CAS 给了我们高性能的可能ABA 问题又给我们挖了个大坑而AtomicStampedReference给了我们填坑的铲子。这就是程序员的宿命——一直在填坑的路上狂奔。‍♂️下次面试官再问你“CAS 有什么问题啊怎么解决啊”你嘴角上扬把这篇文章里的例子也就是那个“喝水”的梗扔出去再反手写个带版本号的代码我敢保证面试官看你的眼神都会变得含情脉脉好了今天就扯到这儿。代码敲累了记得起来活动活动毕竟咱们的腰间盘比 CAS 机制脆弱多了加油吧打工人✨… …文末好啦以上就是我这期的全部内容如果有任何疑问欢迎下方留言哦咱们下期见。… …学习不分先后知识不分多少事无巨细当以虚心求教三人行必有我师焉wished for you successed ⭐️若喜欢我就请关注我叭。⭐️若对您有用就请点赞叭。⭐️若有疑问就请评论留言告诉我叭。版权声明本文由作者原创转载请注明出处谢谢支持

相关新闻

Windows 编程基础:动态链接库

Windows 编程基础:动态链接库

专栏导航 上一篇:第1章,[Win32 章节]:Windows 简史 回到目录 下一篇:第1章 :第一个 Win32 程序,头文件 本节前言 对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其…

2026/10/7 9:38:28 阅读更多 →
对大模型的思考

对大模型的思考

1、深度思考模式是在大模型训练阶段就具备的能力吗?还是后来人们通过提示词啥的添加的? “深度思考模式是在大模型训练阶段就具备的能力吗?还是后来人们通过提示词啥的添加的?” 点击看看灵光怎么说 👉 https://www.l…

2026/10/7 9:37:27 阅读更多 →
Kubeless JVM 运行时实战指南:用 Java 与 Scala 打包并部署 JVM 函数

Kubeless JVM 运行时实战指南:用 Java 与 Scala 打包并部署 JVM 函数

后端云原生微服务 【免费下载链接】kubeless Kubernetes Native Serverless Framework 项目地址: https://gitcode.com/gh_mirrors/ku/kubeless 点击查看 免费下载 Kubeless 原生支持多种语言的 Serverless 函数,除了常见的 Node.js、Python、Go 之外&a…

2026/10/7 9:37:27 阅读更多 →

最新新闻

《论持久战》的精髓与要点毛泽东,1938年5月 · 针对抗日战争战略问题的系统性答复核心命题:不是“只要坚持就能胜利”,而是“在敌强我弱的客观起点上,靠矛盾转化与能动作战,把战略防御走成战略反攻

《论持久战》的精髓与要点毛泽东,1938年5月 · 针对抗日战争战略问题的系统性答复核心命题:不是“只要坚持就能胜利”,而是“在敌强我弱的客观起点上,靠矛盾转化与能动作战,把战略防御走成战略反攻

《论持久战》的精髓与要点毛泽东,1938年5月 针对抗日战争战略问题的系统性答复核心命题:不是“只要坚持就能胜利”,而是“在敌强我弱的客观起点上,靠矛盾转化与能动作战,把战略防御走成战略反攻”《论持久战》写于193…

2026/10/7 10:05:47 阅读更多 →
一文吃透 Linux 软件安装与 Vim 编辑器(含 Ubuntu/CentOS 实操)

一文吃透 Linux 软件安装与 Vim 编辑器(含 Ubuntu/CentOS 实操)

目录 一、在Linux中安装软件包的三种做法: 1.1 包管理器介绍 1.2 Linux的软件生态问题 二、为什么需要国内镜像源 国内主流开源镜像站汇总 三、yum与apt 3.1 Ubuntu: 3.2 安装源: 3.3 清理缓存 四、编辑器vim 4.1 vim的基本概念 4…

2026/10/7 10:05:47 阅读更多 →
五子棋设计

五子棋设计

一.创建窗口1.创建一个类和对象public class Gameui {public static void main(String[] args) {Gameui uinew Gameui();ui.showUI();}}2.创建showui方法public void showUI() {}3.创建一个窗口对象jf,对窗口的属性进行设置Myframe jfnew Myframe();//创…

2026/10/7 10:05:47 阅读更多 →
ponytail技术解析:从基础概念到工程应用

ponytail技术解析:从基础概念到工程应用

我无法根据当前输入生成符合要求的博文。原因如下:项目标题仅为“ponytail”,这是一个英文单词,直译为“马尾辫”,属于常见发型术语;项目正文为空;关键词为空;摘要描述为空;所谓“相…

2026/10/7 10:05:46 阅读更多 →
代码驱动白板视频:rough.js+Playwright+FFmpeg全链路实现

代码驱动白板视频:rough.js+Playwright+FFmpeg全链路实现

1. 这不是动画软件,是用代码一笔一划“写”出来的白板视频你见过的白板视频,大概率是用After Effects加手绘插件、或者用Explain Everything这类工具录屏生成的。但这次我要说的,是另一种路径:整条视频里每一根线条、每一个文字、…

2026/10/7 10:05:46 阅读更多 →
C# WinForm 数据库备份与恢复实战:SQL语句与文件操作两种方式详解

C# WinForm 数据库备份与恢复实战:SQL语句与文件操作两种方式详解

简介:这份资源是面向C# WinForm开发者的数据库备份与恢复实战Demo,基于VS2008实现,适合需要为桌面应用增加数据安全保障能力的初中级开发者参考。示例围绕两种主流方案展开:一是借助SQLDMO这一COM对象模型,通过SQLServ…

2026/10/7 10:04:45 阅读更多 →

日新闻

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:01:58 阅读更多 →
用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →
芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 7:15:40 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 5:29:09 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 9:29:10 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 8:21:32 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 4:21:51 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 1:18:13 阅读更多 →