在Java中CAS机制是如何工作的_Java无锁并发底层原理解析
发布时间 - 2026-01-27 00:00:00 点击率:次CAS通过CPU硬件指令(如x86的CMPXCHG)在一个不可中断周期内原子执行“读值→比较→写值”,依赖volatile保证可见性,无需锁和上下文切换。
CAS 是怎么做到“比较+交换”一步到位的
CAS 不是 Java 语言层面的语法或关键字,而是由 CPU 硬件指令(如 x86 上的 CMPXCHG)直接支持的原子操作。JVM 通过 Unsafe 类暴露了 compareAndSwapInt、compareAndSwapObject 等 native 方法,这些方法最终调用操作系统底层的汇编指令,在一个不可中断的 CPU 周期内完成「读内存值 → 比较 → 写新值」三步——中间不会被线程调度打断。
关键点在于:它依赖 volatile 变量语义保证可见性(每次读都是从主内存取),再靠硬件指令保证原子性。没有锁、没有阻塞、也没有上下文切换开销。
- 不是 Java 字节码实现的,无法在纯 Java 层模拟;必须走
Unsafe+ JNI + 底层汇编链路 - 所有
AtomicInteger、AtomicReference等原子类的incrementAndGet()、compareAndSet()都基于这套机制 - 如果你看到
getAndAddInt方法里那个经典的 do-while 循环,那正是 CAS 失败后自旋重试的体现
为什么 AtomicInteger.addAndGet() 不会丢数据
因为它的底层逻辑本质上是「乐观重试」:先读当前值 oldValue,算出 newValue = oldValue + delta,再用 CAS 尝试把内存位置的值从 oldValue 改成 newValue。只要期间有别的线程抢先改了这个值,CAS 就失败,循环重新读、再算、再试。
public final int addAndGet(int delta) {
int var1;
do {
var1 = this.getIntVolatile(valueOffset);
} while(!this.compareAndSwapInt(valueOffset, var1, var1 + delta));
return var1 + delta;
}
- 如果两个线程同时执行
addAndGet(1),初始值为 0,一个成功把 0→1,另一个读到的是 1 而非 0,CAS 失败后继续重试,最终结果仍是 2 - 这不是“避免竞争”,而是“接受竞争并靠重试解决”——所以高并发下可能自旋很久,吃 CPU
-
volatile保证每次getIntVolatile()都能拿到最新值;否则可能一直读到过期副本,导致无限重试
ABA 问题不是理论陷阱,是真实会崩业务的 Bug
假设一个线程读到值 A,准备 CAS 成 B;但此时另一个线程把 A→B→A 改了两轮。原线程的 CAS 仍会成功,因为它只比数值,不比“有没有被动过”。这在引用类型中尤其危险——比如栈顶节点被弹出又压入同一个对象,表面值没变,实际已不是同一个逻辑状态。
- 典型场景:
AtomicReference管理对象引用时,若
对象被复用(如对象池),极易触发 ABA
- Java 提供了
AtomicStampedReference和AtomicMarkableReference来带版本号/标记位做双重校验 - 别指望靠加日志或 sleep 规避——ABA 是竞态本质,只能靠带版本的 CAS 或换用锁
别在循环体外手动写 CAS 自旋,除非你真懂代价
很多人看到 CAS 原理后,会手写类似 while (!ref.compareAndSet(old, new)) { old = ref.get(); } 的代码。这看似简洁,但容易忽略几个硬伤:
- 没有退避策略:CPU 空转自旋在高争用下会打满核心,而
AtomicInteger等类内部其实做了轻量级 pause 优化(x86 上是PAUSE指令) - 没考虑线程优先级反转风险:低优先级线程长时间占着 CPU 自旋,高优先级线程反而卡住
- 多数业务场景根本不需要裸 CAS——
ConcurrentHashMap、LongAdder、StampedLock已经封装了更稳的模式
真正该自己上 CAS 的情况极少:比如写高性能 RingBuffer、无锁队列,或定制化状态机。其余时候,优先用标准库提供的原子类或并发容器。
# java
# 操作系统
# 字节
# 栈
# 无锁
# 标准库
# 为什么
# 有锁
# jvm
# while
# 封装
# 子类
# volatile
# 循环
# 引用类型
# 线程
# 并发
# 对象
# bug
# 重试
# 读到
# 改了
# 的是
# 几个
# 如果你
# 见性
# 不需要
# 都能
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭
Laravel如何创建自定义中间件?(Middleware代码示例)
Laravel如何为API生成Swagger或OpenAPI文档
简历没回改:利用AI润色让你的文字更专业
软银砸40亿美元收购DigitalBridge 强化AI资料中心布局
Laravel Artisan命令怎么自定义_创建自己的Laravel命令行工具完全指南
Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】
Laravel如何构建RESTful API_Laravel标准化API接口开发指南
Laravel的.env文件有什么用_Laravel环境变量配置与管理详解
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
深入理解Android中的xmlns:tools属性
深圳网站制作的公司有哪些,dido官方网站?
如何用好域名打造高点击率的自主建站?
免费网站制作appp,免费制作app哪个平台好?
三星网站视频制作教程下载,三星w23网页如何全屏?
在centOS 7安装mysql 5.7的详细教程
html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】
JavaScript如何实现错误处理_try...catch如何捕获异常?
胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
如何在阿里云购买域名并搭建网站?
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析
如何在IIS中新建站点并配置端口与IP地址?
Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】
如何在万网ECS上快速搭建专属网站?
如何在 React 中条件性地遍历数组并渲染元素
简单实现Android文件上传
如何用5美元大硬盘VPS安全高效搭建个人网站?
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中
浅析上传头像示例及其注意事项
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
网站制作大概多少钱一个,做一个平台网站大概多少钱?
详解MySQL数据库的安装与密码配置
如何用AWS免费套餐快速搭建高效网站?
如何快速搭建虚拟主机网站?新手必看指南
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
太平洋网站制作公司,网络用语太平洋是什么意思?
如何用景安虚拟主机手机版绑定域名建站?
如何快速查询网站的真实建站时间?
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
如何在宝塔面板中创建新站点?
悟空识字怎么关闭自动续费_悟空识字取消会员自动扣费步骤
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
用yum安装MySQLdb模块的步骤方法
简单实现jsp分页
如何在橙子建站中快速调整背景颜色?
如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】


