《Java 100 天进阶之路》第58篇:原子类与CAS(2026版) 第58篇原子类与CAS2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第57篇CompletableFuture异步编程 |➡️ 下一篇第59篇ThreadLocal原理与内存泄漏待发布️ 本文阅读地图3 分钟速览第57篇搞定了异步编程本篇深入无锁并发编程的核心——原子类与CAS。掌握了CAS和原子类你就掌握了Java并发编程中“性能最优”的线程安全方案模块核心问题一句话回答为什么需要原子类count为什么线程不安全读-改-写三步操作可能被线程调度打断CAS是什么为什么能不加锁实现原子操作CPU原子指令lock cmpxchg硬件保证比较交换不可分割原子类家族有哪些原子类怎么用五大分类基本类型、数组、引用、属性更新器、高性能累加器VarHandleUnsafe 还能用吗JDK 23 已废弃 Unsafe 内存访问方法用 VarHandle 替代ABA问题CAS 有什么坑值变过又变回原样CAS误判为未修改LongAdder高并发计数用什么分段累加分散热点性能是 AtomicLong 的 4 倍以上面试最爱问高频考点有哪些见文末 小节文章目录第58篇原子类与CAS2026版️ 本文阅读地图3 分钟速览一、核心知识点二、生活类比从“图书馆借书”到“CAS无锁借阅”三、CAS 原理从 CPU 指令到 Java 实现3.1 CAS 是什么3.2 CAS 的自旋循环3.3 volatile CAS原子类的底层双引擎3.4 Java 中 CAS 的入口从 Unsafe 到 VarHandle四、原子类全家福五大分类一览4.1 基本类型原子类4.2 数组类型原子类4.3 引用类型原子类4.4 属性更新器Field Updater4.5 高性能累加器五、核心原子类使用示例5.1 AtomicInteger 常用方法5.2 AtomicReference原子更新对象引用5.3 AtomicIntegerArray原子更新数组元素5.4 AtomicIntegerFieldUpdater零内存开销的原子更新六、ABA 问题与解决方案6.1 ABA 问题是什么6.2 解决方案AtomicStampedReference版本号七、LongAdder高并发计数的终极方案7.1 LongAdder vs AtomicLong 性能对比7.2 LongAdder 核心原理7.3 Contended解决伪共享问题7.4 LongAdder 适用场景八、生产级避坑清单九、面试高频考点面试官追问陷阱加分题十、练习题 你的学习进度 下一篇文章预告一、核心知识点为什么需要原子类在多线程环境下一个简单的count操作在字节码层面包含三条指令读、改、写。多个线程同时执行时可能出现“丢失更新”问题——两个线程读到同一个旧值各自加1后写回结果只加了1次。传统解决方案的局限synchronized能保证原子性但代价是线程阻塞与上下文切换在高并发、低竞争的场景下显得“太重”volatile只能保证可见性不能保证复合操作的原子性原子类的核心优势基于CASCompare And Swap无锁算法可以在不加锁的情况下安全地更新变量性能远超 synchronized。CAS 的三要素内存位置 V、预期原值 A、新值 B——如果 V 的值等于 A就更新为 B否则不做任何操作。整个比较与交换是一条 CPU 原子指令不可分割。二、生活类比从“图书馆借书”到“CAS无锁借阅”synchronized 就像传统图书馆的“排队借书”读者想借书必须先到管理员那里排队拿号获取锁拿到号后才能去书架取书。如果管理员正在处理别人所有人都得等着线程阻塞。即使书架上明明有书也得排队——这就是悲观锁。CAS 就像智能图书馆的“自助借阅机”读者不用排队直接去书架取书。取书时机器会自动检查“这本书还在吗有没有被别人借走”如果还在直接取走CAS 成功如果被别人拿走了机器会提示“书已被借走重新选一本吧”CAS 失败重试。整个过程不需要排队等待无锁效率极高。高冲突下的问题如果书店搞“1元抢书”活动成百上千的人同时冲向同一本书。CAS 自助机就会不断提示“书被抢走了再试一次”读者只能原地反复尝试自旋CPU 被大量消耗。这时就需要LongAdder——就像把书分散到多个分店每个分店各备几本读者分散抢购冲突大幅降低。三、CAS 原理从 CPU 指令到 Java 实现3.1 CAS 是什么CAS 全称 Compare And Swap是一条 CPU 原子指令。在 x86 架构中对应的底层指令是lock cmpxchg单核 CPUcmpxchg本身就能保证原子性多核 CPU指令前必须加lock前缀触发 CPU 缓存锁定基于 MESI 缓存一致性协议其他核绝对无法插手面试关键CAS 的原子性由硬件保证不是 Java 层面实现的。3.2 CAS 的自旋循环原子类几乎所有方法都基于“CAS 自旋”实现// AtomicInteger.incrementAndGet() 的简化逻辑for(;;){intoldvalue;// 读intnextold1;// 改if(compareAndSet(old,next)){// CAS 写returnnext;}// CAS 失败 → 继续循环重试}线程不会阻塞失败就重试直到成功——这就是“无锁”的秘密。线程池中同一个任务多次 CAS 失败可能会不断自旋浪费 CPU因此需要结合超时或退避策略避免无限重试。3.3 volatile CAS原子类的底层双引擎原子类内部的值字段都是volatile的privatevolatileintvalue;volatile 的作用保证线程间可见性get()方法总能获取最新值禁止指令重排序配合 CAS 才能正确工作CAS volatile 无锁线程安全CAS 保证原子性volatile 保证可见性。3.4 Java 中 CAS 的入口从 Unsafe 到 VarHandleJDK 8 之前CAS 操作通过sun.misc.Unsafe类的 native 方法实现publicfinalnativebooleancompareAndSwapInt(Objecto,longoffset,intexpected,intx);Unsafe 的问题正如其名它是不安全的——可能导致 JVM 崩溃、未定义行为等。它从未被设计为标准 API却因性能优势被大量框架使用。JDK 23 的重大变革JEP 471 正式将 Unsafe 的内存访问方法标记为废弃Deprecate for Removal。未来版本中这些方法将被移除。推荐替代方案VarHandleJava 9 引入JDK 23 确立为唯一推荐方案。VarHandle 的优势✅ 直接映射 JVM 字节码语义不走反射路径✅ 支持更精细的内存顺序控制acquire/release/opaque✅ 由 JVM 原生支持类型在编译/链接期校验✅ 在 ARM 等架构上可省略不必要的内存屏障⚠️重要提醒JDK 23 起不要再用Unsafe.compareAndSwapInt等方法写新代码。VarHandle 已在 JDK 21 的原子类底层实现中全面替换 Unsafe。VarHandle 使用示例importjava.lang.invoke.MethodHandles;importjava.lang.invoke.VarHandle;publicclassVarHandleDemo{privateintvalue;privatestaticfinalVarHandleVH_VALUE;static{try{VH_VALUEMethodHandles.lookup().findVarHandle(VarHandleDemo.class,value,int.class);}catch(Exceptione){thrownewError(e);}}publicbooleancompareAndSet(intexpected,intnewValue){returnVH_VALUE.compareAndSet(this,expected,newValue);}}四、原子类全家福五大分类一览JUC 原子类分为五大阵营4.1 基本类型原子类类名用途核心方法AtomicInteger原子更新 intincrementAndGet()、getAndIncrement()AtomicLong原子更新 long同上AtomicBoolean原子更新 booleancompareAndSet()4.2 数组类型原子类类名用途AtomicIntegerArray原子更新 int 数组的某个元素AtomicLongArray原子更新 long 数组的某个元素AtomicReferenceArray原子更新引用数组的某个元素4.3 引用类型原子类类名用途AtomicReference原子更新对象引用AtomicStampedReference带版本号的引用解决 ABA 问题AtomicMarkableReference带标记位的引用解决 ABA 问题4.4 属性更新器Field Updater类名用途AtomicIntegerFieldUpdater原子更新类中的 volatile int 字段AtomicLongFieldUpdater原子更新类中的 volatile long 字段AtomicReferenceFieldUpdater原子更新类中的 volatile 引用字段特点不持有任何状态通过反射/ VarHandle 直接操作目标对象的内存零额外内存开销。4.5 高性能累加器类名用途LongAdder高并发下更优的 long 累加性能DoubleAdder高并发下更优的 double 累加性能LongAccumulator自定义累加函数的 long 累加器DoubleAccumulator自定义累加函数的 double 累加器五、核心原子类使用示例5.1 AtomicInteger 常用方法AtomicIntegercounternewAtomicInteger(0);// 自增并返回新值intnewValcounter.incrementAndGet();// 1// 获取并自增intoldValcounter.getAndIncrement();// 返回1内部变为2// 比较并设置booleansuccesscounter.compareAndSet(2,10);// true// 累加指定值intresultcounter.addAndGet(5);// 15// 更新函数式JDK 8counter.updateAndGet(x-x*2);// 305.2 AtomicReference原子更新对象引用AtomicReferenceStringrefnewAtomicReference(A);ref.compareAndSet(A,B);// true5.3 AtomicIntegerArray原子更新数组元素AtomicIntegerArrayarraynewAtomicIntegerArray(10);intoldarray.getAndAdd(0,5);// 索引0的元素加5返回旧值5.4 AtomicIntegerFieldUpdater零内存开销的原子更新classUser{volatileintage;}AtomicIntegerFieldUpdaterUserupdaterAtomicIntegerFieldUpdater.newUpdater(User.class,age);UserusernewUser();updater.compareAndSet(user,0,18);// 原子更新无需加锁六、ABA 问题与解决方案6.1 ABA 问题是什么CAS 只比较最终值不关心中间过程。假设某变量从 A 变成 B又从 B 变成 A线程 T1 读到值是 A线程 T2 将 A → B → AT1 执行 CAS发现当前值还是 A → 误判为“未被修改”→ 成功更新真实危害在栈、链表等数据结构中即使头节点值相同但内部结构可能已经发生了变化。6.2 解决方案AtomicStampedReference版本号通过引入**版本号stamp**来区分“相同值但不同历史”AtomicStampedReferenceStringrefnewAtomicStampedReference(A,0);int[]stampHoldernewint[1];StringoldValueref.get(stampHolder);// 获取值和版本号// 每次更新同时更新值和版本号booleansuccessref.compareAndSet(oldValue,// 预期值B,// 新值stampHolder[0],// 预期版本号stampHolder[0]1// 新版本号递增);核心原理CAS 同时校验值 版本号即使值回到 A版本号已变CAS 依然失败。AtomicMarkableReference vs AtomicStampedReference特性AtomicMarkableReferenceAtomicStampedReference标记类型booleantrue/falseint可自增的版本号能区分几次变更最多区分 1 次可区分无限次适用场景仅需知道“是否被修改过”需要知道“被修改了多少次”⚠️注意如果业务不需要区分历史如计数器仅关心最终值直接用AtomicInteger更轻量。AtomicStampedReference适合无锁栈、无锁队列等需要追踪结构变化的场景。七、LongAdder高并发计数的终极方案7.1 LongAdder vs AtomicLong 性能对比基准测试结果5000 线程 × 50000 次累加LongAdder 的效率是 AtomicInteger 的4 倍左右。AtomicLong 的性能瓶颈所有线程竞争同一个内存地址CAS 失败时大量线程陷入自旋空转CPU 空转浪费。频繁的 CAS 操作导致 CPU 缓存行在多核之间失效引发“总线风暴”严重影响所有核的性能。LongAdder 的设计哲学分段累加分散热点。7.2 LongAdder 核心原理低并发时无竞争直接 CAS 更新base字段零额外内存开销。首次竞争时CAS 更新base失败 → 初始化Cell[]数组长度 2。高并发时每个线程通过哈希定位到自己的Cell各自累加最后求和。7.3 Contended解决伪共享问题Cell类上标注了Contended注解JVM 在对象布局时自动填充空白字节确保每个 Cell至少独占一个 CPU 缓存行64 字节避免伪共享——一个线程修改 Cell0 导致 Cell1 的缓存行失效7.4 LongAdder 适用场景场景推荐说明写多读少计数器、统计✅ 强烈推荐分散冲突性能远超 AtomicLong读写均衡或需强一致性⚠️ 谨慎最终值需遍历所有 Cell 累加可能略慢需要精确的实时值❌ 不推荐sum()是弱一致性累加过程中可能有并发修改八、生产级避坑清单✅ 原子类与 CAS 生产环境使用规范 1. 高冲突场景慎用 CAS → 大量自旋会烧 CPU考虑 LongAdder 或锁 2. LongAdder 不保证强一致性 → sum() 是弱一致性不适合精确实时值 3. ABA 问题 → 无锁栈/队列用 AtomicStampedReference计数器用 AtomicInteger 4. 不要在 JDK 23 使用 Unsafe → 用 VarHandle 替代 5. VarHandle 实例必须缓存复用 → 每次重建慢 20-50 倍 6. 原子类只保证单变量原子性 → 多变量操作仍需要锁或 CAS 循环 7. 属性更新器字段必须是 volatile → 否则抛异常 8. LongAdder 适合写多读少 → 读多写少场景 AtomicLong 更合适九、面试高频考点Q1CAS 是什么为什么能保证原子性CASCompare And Swap是一条 CPU 原子指令需要三个操作数内存位置 V、预期值 A、新值 B。当 V 的值等于 A 时原子地更新为 B。原子性由硬件保证——在 x86 中对应lock cmpxchg指令。Q2CAS 的优缺点✅ 优点无锁、无上下文切换、低竞争下性能极高。❌ 缺点① ABA 问题② 高冲突下自旋消耗 CPU③ 只能保证单个变量的原子性。Q3ABA 问题是什么如何解决ABA 问题变量从 A 变成 B 又变回 ACAS 误判为未被修改。解决方案用AtomicStampedReference版本号或AtomicMarkableReference标记位。Q4LongAdder 为什么比 AtomicLong 快LongAdder 采用分段累加设计低并发时直接 CAS 更新 base高并发时将累加操作分散到多个 Cell 上每个线程各自累加自己的 Cell最后求和。避免所有线程竞争同一个变量。Q5Unsafe 为什么被废弃用什么替代Unsafe 的内存访问方法可能导致 JVM 崩溃、未定义行为。JDK 23 已将其标记为废弃。替代方案是VarHandleJava 9 引入JDK 23 确立为唯一推荐。面试官追问陷阱加分题追问1“AtomicStampedReference的 stamp 可以自己随意设置吗” 可以但要保证每次修改都更新 stamp且建议严格递增不能用 0/1 交替否则仍可能漏判。stamp 的语义由业务逻辑控制框架不自动管理。追问2“compareAndSet和weakCompareAndSet有什么区别”compareAndSet提供强顺序保证等价于 volatile 写 CAS行为可预测。weakCompareAndSet可能“假失败”值未变也返回 false适合自旋循环场景在 ARM 等架构上性能更高。高频自旋用weakCompareAndSet更高效。追问3“LongAdder 的sum()返回的值是精确的吗”不精确。sum()在累加 Cell 的过程中其他线程可能还在修改 Cell所以结果是弱一致性的。如果需要精确值用AtomicLong或加锁。十、练习题代码题用AtomicInteger实现一个线程安全的计数器100 个线程各累加 10000 次验证结果是否为 1000000。分析题某系统使用AtomicLong作为高并发计数器发现 CPU 使用率飙升。可能原因是什么如何优化 思路高并发下大量线程 CAS 失败并自旋CPU 空转。优化方案改用LongAdder通过分段累加分散竞争。场景设计实现一个无锁栈Lock-Free Stack支持push和pop操作要求正确处理 ABA 问题。 思路使用AtomicStampedReference同时管理栈顶引用和版本号每次push/pop时更新版本号。CAS 操作同时校验引用和版本号避免 ABA。 你的学习进度当前第58篇 / 共108篇 ·进阶篇并发编程与JUC详解第51~60篇✅ 已完成基础篇44篇 第45~58篇 正在学第58篇⏳ 待学习第59~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 下一篇文章预告下一篇《第59篇ThreadLocal原理与内存泄漏》内容简介ThreadLocal 底层原理ThreadLocalMap、Entry 弱引用、内存泄漏原因与解决方案、InheritableThreadLocal 与 TransmittableThreadLocal、生产环境最佳实践。多线程专题持续深入拿下 ThreadLocal《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送