cas在atomicinteger中通过volatile字段、字段偏移量和unsafe.compareandswapint三者协同实现原子操作,本质是映射cpu指令的乐观无锁机制,适用于单变量低频写场景。

Java 中的 CAS 机制在 AtomicInteger 里不是“封装逻辑”,而是直接映射到 CPU 硬件指令的原子操作,它靠的是 Unsafe 类 + volatile 字段 + 自旋重试,三者缺一不可。
CAS 在 AtomicInteger 中怎么落地
核心就三点:
- volatile int value:保证每次读取都是最新值,避免线程缓存旧值导致比较失败
-
valueOffset:通过
Unsafe.objectFieldOffset()获取value字段在对象内存中的真实偏移量,让底层能精准定位并操作该字段 -
Unsafe.compareAndSwapInt():真正执行 CAS 的 native 方法,由 JVM 调用操作系统/硬件指令(如 x86 的
cmpxchg),整个“比较+替换”一步完成,不可中断
比如 incrementAndGet() 的实际行为是:
先用 getIntVolatile() 读当前值 → 计算新值 → 调用 compareAndSwapInt() 尝试更新 → 失败就循环重试,直到成功。
为什么不用 synchronized 却能线程安全
因为 CAS 是乐观策略:不锁住资源,而是“先干再验”。它默认没有竞争,只在发现值被改过时才重试。这带来两个关键效果:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 无阻塞:线程不会挂起、不会触发上下文切换,适合低竞争场景
- 无锁:不依赖操作系统互斥量(mutex),规避了内核态开销
- 但要注意:高竞争下自旋会浪费 CPU,此时可能不如
synchronized或ReentrantLock
AtomicInteger 的典型应用场景
它不是万能计数器,适用的是“单变量、高频读+低频写、无需复合逻辑”的场景:
- 请求计数器:统计 API 调用量、限流器中的当前请求数
- 序列号生成:订单号、日志流水号等简单递增 ID(注意:非全局唯一 ID 场景需配合时间戳或机器 ID)
-
状态标记位:用 0/1 表示开关状态(如初始化完成标志),配合
compareAndSet(0, 1)实现一次性设置 - 资源配额控制:比如剩余连接数、令牌桶余量,每次扣减前检查是否足够
-
并发集合内部计数:如
ConcurrentHashMap的 size 计算就依赖多个AtomicLong分段累加
容易踩的坑和注意事项
用对地方才能发挥优势,常见误区包括:
-
误把 ++ 操作当成原子:即使变量是
AtomicInteger,写成counter.get() + 1再set()就失去原子性,必须用incrementAndGet()或addAndGet() -
链式操作仍非原子:比如 “先 get 再判断再 set”,中间可能被其他线程插队,应改用
compareAndSet(expected, newValue)做条件更新 -
忽略 ABA 问题:值从 A→B→A,CAS 会误判成功。一般业务中整型计数不敏感,但涉及引用类型(如栈顶节点)时需用
AtomicStampedReference - 过度使用:如果操作涉及多个字段联动(如账户余额扣减 + 订单状态更新),CAS 无法覆盖,得回归锁或事务
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










