AtomicInteger 更轻量因基于CAS指令,避免线程挂起与上下文切换;但高竞争下重试耗CPU,适用于计数器等简单操作,不适用复合逻辑。

AtomicInteger 为什么比 synchronized 更轻量
因为 AtomicInteger 底层调用的是 CPU 提供的 CAS(Compare-And-Swap)指令,不涉及操作系统线程挂起/唤醒,避免了锁带来的上下文切换开销。但要注意:CAS 在高竞争下会不断重试,反而可能比锁更耗 CPU。
典型适用场景是计数器、状态标志位这类简单读-改-写操作,比如秒杀库存扣减、请求统计。不适合复合逻辑(如“先读再判断再更新”),那得用 synchronized 或 ReentrantLock。
常见错误是误以为 incrementAndGet() 能替代所有同步场景——它只保证单个原子操作,不保证多个原子操作之间的顺序或可见性组合。
getAndIncrement 和 incrementAndGet 的区别在哪
两者都执行“加 1 并返回旧值”或“加 1 并返回新值”,但语义和返回值不同:
立即学习“Java免费学习笔记(深入)”;
-
getAndIncrement()返回自增前的值(类似i++) -
incrementAndGet()返回自增后的值(类似++i)
性能上无差别,选哪个纯看业务需要。例如生成唯一 ID 时常用 incrementAndGet(),而做轮询索引时可能用 getAndIncrement() 避免额外减 1。
注意:它们都不是“线程安全的 ++ 运算符替代品”——Java 中没有原生的原子 ++ 操作符,必须显式调用这些方法。
AtomicReference 用错引用比较会出什么问题
AtomicReference 的 compareAndSet(expected, updated) 是按引用地址比较(==),不是按内容(.equals())。如果传入一个新构造的对象作为 expected,即使字段值相同,也会失败。
常见踩坑点:
- 用
new String("abc")作期望值,而当前值是字符串常量池里的"abc" - 在循环中反复 new 对象用于 compareAndSet,导致永远无法匹配
- 误以为它能自动处理对象内部字段的原子更新(不能,要用
AtomicReferenceFieldUpdater或VarHandle)
AtomicReferenceref = new AtomicReference<>("hello"); // ❌ 错误:new String 创建新对象,引用不同 ref.compareAndSet(new String("hello"), "world"); // 返回 false // ✅ 正确:复用原引用或使用 intern() ref.compareAndSet("hello", "world"); // 返回 true
AtomicLongArray 在批量计数时怎么避免伪共享
多核 CPU 缓存以 cache line(通常 64 字节)为单位加载数据。AtomicLongArray 的相邻元素若落在同一 cache line,会导致不同线程更新不同下标时相互干扰(false sharing),性能骤降。
解决方式不是换工具,而是结构对齐:
- 手动 padding:每个 long 后填充 7 个 long,确保每个有效元素独占 cache line(但浪费内存)
- 用
jdk.internal.vm.annotation.Contended(JDK 8+,需开启-XX:-RestrictContended) - 更实际的做法是:避免高频更新密集下标;或改用分段计数(如 LongAdder,它内部就用了类似 padding 的策略)
LongAdder 比 AtomicLong 更适合高并发累加场景,但它不是“更高级的 AtomicLong”,而是牺牲了实时一致性来换吞吐——它的 sum() 不是强一致的。










