怎么利用 AtomicReference 配合 CAS 算法实现无锁状态下的复杂业务逻辑更新

冬瑶姑娘_2242

冬瑶姑娘_2242

2026-04-20

150人浏览

原创

atomicreference 的 cas 操作本质是 compareandset(v, e, n),即仅当当前值等于预期值时才原子更新,必须配合 do-while 循环重试、不可变状态对象封装及显式业务校验才能实现真正无锁安全。

怎么利用 atomicreference 配合 cas 算法实现无锁状态下的复杂业务逻辑更新

AtomicReference 的 CAS 操作本质是 compareAndSet(V, E, N)

它不是“直接改值”,而是原子性地执行「如果当前值等于预期值,才设为新值」。这意味着你无法绕过状态校验——哪怕只是想给一个字段加 1,也得先读、再算、再 CAS,失败就得重试。很多初学者误以为 AtomicReference 像普通变量一样能链式赋值或批量更新,结果写出非原子的中间态逻辑,比如先改对象某个字段再 set,这完全破坏了无锁前提。

典型错误现象:compareAndSet 频繁返回 false,但没做重试或 fallback;或者把多个字段更新拆成两次 compareAndSet,导致中间态被其他线程读到脏数据。

  • 必须用不可变对象(如自定义 State 类)封装所有相关字段,让一次 compareAndSet 覆盖整个业务状态
  • compareAndSet 的预期值必须是上一次 get() 的结果,不能复用缓存的老值
  • 若业务逻辑涉及计算(如“余额减扣后不能为负”),需在循环体内做条件判断,失败则重新读取最新状态再试

实现带业务校验的无锁更新必须用 do-while 循环重试

CAS 天然适合乐观场景,但业务规则(比如库存不能超卖、状态只能从 WAITING → PROCESSING)必须由你显式编码进重试逻辑里,JVM 不会自动帮你拦住非法流转。不写循环,compareAndSet 失败就等于放弃更新——这不是并发安全,这是丢数据。

使用场景:订单状态机推进、分布式任务调度器的状态跃迁、共享资源配额扣减。

  • 每次循环开头调用 get() 获取当前完整状态对象
  • 基于当前状态做业务判断(如 if (current.status == WAITING && current.remain >= need))
  • 构造新状态对象(务必新建实例,不可复用或修改原对象)
  • 调用 compareAndSet(current, next),失败则继续下一轮

示例片段:

鲜艺AI抠图
鲜艺AI抠图

鲜艺AI抠图是一款AI图片处理工具,免费 AI 抠图工具,支持离线安装与使用。

下载
do {
    State current = ref.get();
    if (current.status != State.WAITING || current.balance <h3>AtomicReference 无法保证复合操作原子性,多变量更新必须打包成单个对象</h3><p>有人试图用两个 <code>AtomicReference</code> 分别管「余额」和「版本号」,再靠两次 CAS 串行更新——这是错的。两次 CAS 之间存在时间窗口,其他线程可能在中间修改任一字段,导致状态不一致。CAS 只保证「对同一个引用地址的一次写入」是原子的,不负责跨引用协调。</p><p>参数差异:传给 <code>compareAndSet</code> 的 <code>expectedValue</code> 和 <code>newValue</code> 必须是同一类不可变对象实例,且所有业务相关字段都得包含在内。哪怕只多一个时间戳或请求 ID,也要进这个对象。</p>
  • 避免使用可变对象(如 HashMap 或普通 class)作为 AtomicReference 的泛型类型
  • 构造新状态时不要复用旧对象字段(比如 next.items = new ArrayList(current.items)),否则浅拷贝仍可能引发并发问题
  • 如果状态对象过大(如含百级 List),频繁 GC 和对象分配会成为瓶颈,此时应评估是否真需要无锁,或改用分段锁+更粗粒度状态

ABA 问题在业务逻辑中往往比理论更隐蔽

标准 ABA 场景(A→B→A)容易被识别,但业务中常见的是「语义 ABA」:比如订单状态从 CREATED → PAID → REFUNDED → CREATED,表面看又回到 CREATED,但这次的 CREATED 已携带不同上下文(如新支付渠道、不同用户 ID)。如果只比对状态枚举值,CAS 会成功,但业务已错乱。

性能影响:加版本号或时间戳虽能破 ABA,但会让每次更新都强制 bump 字段,增加对象大小和 GC 压力;用 AtomicStampedReference 则要额外维护 stamp,代码更重。

  • 优先从业务建模规避:把「重入 CREATED」设计为新状态(如 RECREATED),而非复用旧值
  • 若必须用 stamp,确保 stamp 变更与业务关键事件强绑定(如每次支付生成唯一 traceId,作为 stamp)
  • 不要用 System.nanoTime() 当 stamp——精度不够且可能回拨

真正难处理的从来不是 CAS 指令本身,而是你怎么定义“一次完整的业务状态”,以及是否愿意为它付出对象建模和重试逻辑的成本。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

9897

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7022

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6192

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1064

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

888

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1296

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2589

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

19931

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1155

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习