cas是cpu硬件指令(如x86的cmpxchg)实现的原子操作,java通过unsafe调用底层硬件原语,配合volatile保证可见性,以自旋重试替代锁机制,实现无阻塞并发控制。

CAS 不是 Java 自己“写出来”的逻辑,而是把“读值—比对—写入”这三步,交给 CPU 用一条指令原子执行。它不靠锁排队,也不让线程挂起,靠的是硬件原语的硬保证。
CPU 指令才是原子性的真正来源
Java 层面看到的 compareAndSwapInt,底层对应的是 x86 的 CMPXCHG 这类指令。这条指令在电路层面完成三件事:从内存读当前值、和寄存器里的预期值比较、相等就写新值——整个过程不可中断。
- 如果目标变量在 CPU 缓存行中且处于独占状态(MESI 协议的 M 状态),CPU 只锁住该缓存行,开销极小
- 只有当缓存失效或跨核竞争激烈时,才升级为总线锁,但现代处理器绝大多数情况走缓存锁路径
- JVM 不可能靠软件模拟这种原子性,Unsafe 类的 native 方法,本质就是调用操作系统封装好的硬件指令入口
Java 如何把硬件能力变成可用的原子操作
AtomicInteger.incrementAndGet() 看似简单,背后是一套协作机制:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 先用 volatile 读取当前值——确保看到最新数据,不从线程本地缓存取旧值
- 算出新值(比如 +1)
- 调用 CAS:仅当内存里还是刚才读到的值,才更新;否则失败,返回 false
- 失败后不等待,而是重新读、再算、再试——这就是自旋重试,责任交给上层代码
为什么说它是“无锁”却能保证正确性
传统锁像收费站,车来了要排队、登记、放行;CAS 更像自助闸机:你刷卡(提交预期值),机器当场判断卡号是否匹配(比较),匹配就抬杆(写入),不匹配就让你重刷(重试)。全程没有调度介入,也没有线程阻塞。
- 它堵住了 volatile 解决不了的“读-改-写”漏洞:两个线程同时读到 5,各自 +1 后都写 6,结果丢了一次更新;CAS 要求“必须还是 5 才能写 6”,第二个线程发现已是 6,自然重来
- 成功时一次完成,失败时不消耗锁资源,适合计数器、开关标志、引用替换等单变量高频读低频写的场景
- 不是“完全没有同步”,而是把同步逻辑从 JVM 内部移到应用层,更轻量也更可控
不能忽视的现实约束
CAS 强大,但不是银弹:
- ABA 问题:值从 A→B→A,CAS 误判为未变。可配合 AtomicStampedReference 加版本号缓解
- 只能保护单个内存地址。多个字段需同步更新?得包装成对象用 AtomicReference,或换 LongAdder、StampedLock 等更高级结构
- 高冲突下自旋会空耗 CPU。生产环境建议结合 Thread.onSpinWait() 或指数退避,避免死循环压满核心
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










