atomicinteger的incrementandget比synchronized快,因其底层使用cpu原子指令(如x86的lock xadd),避免线程挂起、锁队列和上下文切换;而synchronized在竞争激烈时升级为重量级锁,需操作系统介入。

AtomicInteger 的 incrementAndGet 为什么比 synchronized 快
因为 incrementAndGet 底层调用的是 CPU 的 LOCK XADD 指令(x86)或 LDAXR/STLXR(ARM),直接在硬件层面保证原子性,不涉及线程挂起、锁队列、上下文切换这些高开销操作。而 synchronized 在竞争激烈时会升级为重量级锁,触发操作系统介入。
实操建议:
- 仅对单个变量的简单读-改-写(如计数、状态标志)优先用
AtomicInteger/AtomicBoolean - 避免把多个
getAndIncrement()拼成“伪原子逻辑”,比如a.get() + b.get()再 set,这中间 a/b 都可能被其他线程修改 - 注意
compareAndSet(expected, updated)的 ABA 问题:值从 A→B→A,CAS 会成功,但语义可能已错。必要时用AtomicStampedReference
AtomicReference 用作无锁栈或队列的关键约束
AtomicReference 本身不提供结构操作,你要自己实现 push/pop 或 offer/poll,必须严格满足两个条件:一是所有状态变更只能通过 compareAndSet 完成;二是每次 CAS 前必须重新读取当前值(不能复用旧快照)。
常见错误现象:NullPointerException 或无限重试循环——往往是因为节点引用被其他线程置为 null 后,你还拿旧的 next 字段去 CAS。
立即学习“Java免费学习笔记(深入)”;
使用场景示例(无锁栈 push):
public void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> currentHead;
do {
currentHead = head.get(); // 每次都重读
newHead.next = currentHead;
} while (!head.compareAndSet(currentHead, newHead)); // 失败就重试
}
注意:这种结构无法避免 ABA,且对 GC 压力略高(大量短命节点);生产环境推荐直接用 ConcurrentLinkedQueue。
AtomicIntegerFieldUpdater 的字段访问限制与性能取舍
AtomicIntegerFieldUpdater 允许对普通对象的 volatile int 字段做原子更新,但它要求字段必须是 public static final 修饰的 updater 实例,并且目标字段本身必须是 volatile、非 static、非 final。
参数差异和陷阱:
- 字段名必须是字符串字面量(
"count"),编译期不校验,写错运行时报RuntimeException - 字段不能是 private —— 即使在同一类里,也必须是 package-private 或 protected/public
- 相比直接 new
AtomicInteger,它省了对象头开销,适合高频更新、对象数量极大的场景(如百万级连接的 Netty Channel 状态计数) - 但调试困难:IDE 无法跳转到字段定义,日志打印时也看不到 updater 和字段的绑定关系
LongAdder 比 AtomicInteger 更适合高并发累加的底层原因
LongAdder 不是“更原子”,而是用空间换时间:它把一个总和拆成多个 Cell(类似分段 hash),线程先尝试往自己的槽里加,冲突时才退化为对 base 的 CAS。因此在写远多于读的场景(如监控指标计数),吞吐量可提升数倍。
但要注意:
-
sum()是最终一致性读,返回的是当前所有 Cell 的快照和,期间其他线程可能还在写 - 不能替代
AtomicInteger的getAndIncrement()这类需要返回旧值的场景——LongAdder没有这类方法 - 如果只有一两个线程更新,
LongAdder反而比AtomicInteger慢,因多了 cell 查找和初始化开销
真正容易被忽略的是内存占用:每个活跃线程可能独占一个 Cell,极端情况下(数千线程)会分配上百个缓存行,对 L1/L2 Cache 不友好。别在低并发小对象上盲目替换。











