登录
首页 >  文章 >  java教程

Java原子操作与Atomic类使用解析

时间:2026-01-24 13:21:37 175浏览 收藏

偷偷努力,悄无声息地变强,然后惊艳所有人!哈哈,小伙伴们又来学习啦~今天我将给大家介绍《Java原子操作与Atomic类详解》,这篇文章主要会讲到等等知识点,不知道大家对其都有多少了解,下面我们就一起来看一吧!当然,非常希望大家能多多评论,给出合理的建议,我们一起学习,一起进步!

AtomicInteger的incrementAndGet更轻量因其基于CPU的CAS指令,避免线程挂起与上下文切换;但仅保证单操作原子性,不支持多操作事务,高争用时自旋耗CPU。

Java并发编程中的原子操作与Atomic类

AtomicInteger 的 incrementAndGet 为什么比 synchronized 更轻量

因为 incrementAndGet 底层调用的是 CPU 的 cmpxchg(Compare-and-Swap)指令,不涉及线程挂起、锁队列、上下文切换这些重量级操作。而 synchronized 在竞争激烈时会升级为重量级锁,触发操作系统介入。

但要注意:它只保证单个操作的原子性,不能组合多个 AtomicInteger 操作成一个原子事务。比如「先 get 再 set」就不是原子的,必须用 compareAndSet 手动实现。

  • 适合场景:计数器、序列号生成、状态标志位更新
  • 不适合场景:需要同时更新 AtomicIntegerAtomicBoolean 的复合逻辑
  • 性能影响:在高争用(多线程反复失败重试)下,CAS 自旋会浪费 CPU,此时 synchronized 反而更稳

AtomicReference 的 compareAndSet 容易忽略的引用相等性陷阱

compareAndSet 判断的是引用是否相等(==),不是内容相等(.equals())。如果传入一个新构造但字段值相同的对象,比较一定失败。

AtomicReference<User> ref = new AtomicReference<>(new User("Alice", 25));
User old = ref.get();
User update = new User("Alice", 25); // 字段相同,但 new 出来的新对象
boolean success = ref.compareAndSet(old, update); // false!old != update

解决办法只有两个:

  • 确保旧值是 ref.get() 返回的那个实例,不要自己 new 或从别处重建
  • 若必须基于内容做条件更新,改用 AtomicStampedReference + 版本号,或退回到 synchronized 块中做完整判断

AtomicLongArray 与普通 long[] 在缓存行伪共享上的差异

数组元素在内存中连续排列,AtomicLongArray 的每个 long 元素都支持独立 CAS,但若多个线程频繁更新相邻索引(如 index=0index=1),可能落在同一 CPU 缓存行(通常 64 字节),引发伪共享(false sharing)——一个线程修改导致另一线程的缓存行失效,反复刷新。

普通 long[] 没这问题,因为它本身不提供原子方法;但一旦你用 synchronized 或其他同步机制包裹访问,伪共享影响就转移到锁上了。

  • 规避方式:对 AtomicLongArray 做索引间隔设计(如只用偶数下标),或手动填充(@Contended,需 JVM 参数开启)
  • 验证工具:JMH + perfasm 可观察缓存行失效次数
  • 注意:AtomicIntegerArray 同理,只是单位是 4 字节

AtomicIntegerFieldUpdater 的反射开销和使用约束

AtomicIntegerFieldUpdater 允许对已有类的 volatile int 字段做原子更新,避免为每个实例都创建 AtomicInteger 对象,节省内存。但它依赖反射,首次调用 newUpdater 有明显开销,且字段必须满足严格条件:

  • 字段必须是 volatile int(不能是 final、不能是 private 除非同包,不能是 static)
  • 调用 compareAndSet 时,目标对象不能为 null,否则抛 NullPointerException
  • updater 实例应静态复用,不要每次临时 new

典型误用:

class Counter {
    volatile int count = 0;
}
// 错误:字段是 private,且不在 updater 同包
AtomicIntegerFieldUpdater<Counter> updater =
    AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count"); // IllegalAccessException

真正安全的做法是把字段设为 protected 或包级私有,并确保 updater 类与目标类在同一个包。

复杂点在于:它把原子性“外挂”到普通字段上,既省空间又埋了反射和可见性规则的雷——稍不留意,运行时才暴露。

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。

前往漫画官网入口并下载 ➜
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>