
🍃 予枫 :个人主页
📚 个人专栏 : 《Java 从入门到起飞》《读研码农的干货日常》《Java 面试刷题指南》
💻 Debug 这个世界,Return 更好的自己!
引言
家人们谁懂啊!Java并发面试里,原子类绝对是"常客",尤其是AtomicInteger和LongAdder,面试官必问"两者有啥区别""什么时候用哪个"。很多人只会用AtomicInteger,对LongAdder一知半解,被追问原理直接卡壳。今天就接地气拆解,从底层原理、实战用法到适用场景,再加上面试追问,帮你彻底吃透这两个核心原子类!
文章目录
- 引言
- 一、JAVA原子类核心认知
- [二、ATOMICINTEGER 原理与实战](#二、ATOMICINTEGER 原理与实战)
-
- [2.1 核心原理(结合源码,不搞复杂)](#2.1 核心原理(结合源码,不搞复杂))
- [2.2 实战用法(直接上手,复制就能用)](#2.2 实战用法(直接上手,复制就能用))
- [2.3 AtomicInteger执行流程(mermaid图直观看)](#2.3 AtomicInteger执行流程(mermaid图直观看))
- [三、LONGADDER 原理与实战](#三、LONGADDER 原理与实战)
-
- [3.1 核心原理(重点:分段累加,减少自旋)](#3.1 核心原理(重点:分段累加,减少自旋))
- [3.2 实战用法(和AtomicInteger类似,上手简单)](#3.2 实战用法(和AtomicInteger类似,上手简单))
- [3.3 LongAdder执行流程(mermaid图拆解)](#3.3 LongAdder执行流程(mermaid图拆解))
- [四、ATOMICINTEGER VS LONGADDER(核心区别,面试必背)](#四、ATOMICINTEGER VS LONGADDER(核心区别,面试必背))
- 五、面试官追问环节(实战价值拉满,直接背)
- 六、总结
一、JAVA原子类核心认知

先一句话搞懂:Java原子类是无需加锁就能保证并发安全的工具类,底层依赖CAS机制(之前讲过的Compare And Swap)或分段累加思想,解决多线程下变量修改的竞态问题,避免synchronized的锁开销,广泛用于并发编程场景。
核心特点(敲重点💡):
- 原子性:保证变量的修改操作是原子的,不会出现多线程并发修改的中间态
- 无锁化:大部分原子类基于CAS实现,无需加锁,性能优于synchronized
- 易用性:API简洁,直接调用方法就能实现原子操作,无需手动处理并发安全
常见的Java原子类分类(简单提一嘴,重点讲核心两个):
- 基本类型原子类:AtomicInteger、AtomicLong、AtomicBoolean(最常用)
- 引用类型原子类:AtomicReference、AtomicStampedReference(解决ABA问题)
- 累加器类:LongAdder、DoubleAdder(高并发场景首选)
二、ATOMICINTEGER 原理与实战
AtomicInteger是最常用的原子类,核心作用是保证int类型变量的原子修改,底层完全依赖CAS机制实现,也是我们入门原子类的首选。
2.1 核心原理(结合源码,不搞复杂)
AtomicInteger的底层核心是「CAS + 自旋」,和之前讲的CAS原理一脉相承,来看关键源码(JDK8):
java
public class AtomicInteger extends Number implements java.io.Serializable {
// 底层依赖Unsafe工具类,直接操作内存
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 变量值在内存中的偏移量,用于定位内存地址
private static final long valueOffset;
static {
try {
// 计算value变量的内存偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 要操作的核心变量,用volatile修饰,保证可见性
private volatile int value;
// 核心原子自增方法,底层调用CAS
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// 核心CAS方法(底层调用CPU原子指令)
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
关键解读(用大白话讲,不绕弯):
- value变量用volatile修饰:保证多线程下变量的可见性,一个线程修改后,其他线程能立即看到最新值
- 依赖Unsafe工具类:直接操作内存地址,实现底层CAS操作
- 自旋机制:当CAS修改失败时,会循环重试,直到修改成功(比如incrementAndGet方法,本质是循环CAS)
2.2 实战用法(直接上手,复制就能用)
话不多说,上代码,常见的原子操作(自增、自减、比较并交换)都给大家写好了:
java
public class AtomicIntegerDemo {
public static void main(String[] args) throws InterruptedException {
// 初始化原子类,初始值为0
AtomicInteger atomicInt = new AtomicInteger(0);
// 1. 原子自增(常用,多线程下安全)
atomicInt.incrementAndGet(); // 结果:1
// 2. 原子自减
atomicInt.decrementAndGet(); // 结果:0
// 3. 原子累加(加5)
atomicInt.addAndGet(5); // 结果:5
// 4. 比较并交换(预期值5,修改为10,成功返回true)
boolean success = atomicInt.compareAndSet(5, 10);
System.out.println("CAS修改是否成功:" + success); // true
System.out.println("最终值:" + atomicInt.get()); // 10
// 多线程测试(验证线程安全)
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
atomicInt.incrementAndGet();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
atomicInt.incrementAndGet();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("多线程自增后的值:" + atomicInt.get()); // 2010(无线程安全问题)
}
}
2.3 AtomicInteger执行流程(mermaid图直观看)
是
否
初始化AtomicInteger,value=0
线程调用incrementAndGet方法
重新获取内存中value的当前值v,自旋重试
CAS比较:内存值v == 预期值v?
将内存值修改为v+1,操作成功
返回修改后的值v+1

三、LONGADDER 原理与实战
LongAdder是JDK8新增的原子累加器,专门解决高并发场景下AtomicInteger(或AtomicLong)自旋开销过大的问题,性能比AtomicInteger更优,尤其适合"高并发、多线程频繁累加"的场景。
3.1 核心原理(重点:分段累加,减少自旋)
LongAdder的核心思想是「分段累加」,打破了AtomicInteger"单个变量CAS自旋"的瓶颈,具体原理如下:
- 内部维护一个数组(cells),每个数组元素都是一个独立的累加单元(类似一个小的计数器)
- 多线程并发累加时,会通过哈希算法,将线程分配到不同的累加单元(cells[i])
- 每个线程只操作自己对应的累加单元,减少CAS自旋冲突(不再所有线程竞争同一个变量)
- 当需要获取最终结果时,将所有累加单元的值相加,得到总结果
简单说:AtomicInteger是"一个人干所有活,大家都抢着用",LongAdder是"多个人分工干,每人干一点,最后汇总",高并发下效率自然更高。

来看核心源码(简化版,看懂关键即可):
java
public class LongAdder extends Striped64 implements Serializable {
// 累加操作,核心方法
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
// 1. 先尝试操作base变量(低并发时,类似AtomicLong,减少数组开销)
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
// 2. 高并发时,获取当前线程对应的Cell,进行累加
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
// 3. 若Cell为空或CAS失败,扩容数组或创建新Cell
longAccumulate(x, null, uncontended);
}
}
// 获取最终结果:汇总所有Cell的值 + base的值
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
for (Cell c : as) {
if (c != null)
sum += c.value;
}
}
return sum;
}
// 内部累加单元,每个Cell都是一个独立的CAS单元
static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// ... 省略其他代码
}
}
3.2 实战用法(和AtomicInteger类似,上手简单)
LongAdder的API和AtomicInteger基本一致,使用起来几乎没有学习成本,重点看高并发场景的表现:
java
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
// 初始化LongAdder,初始值为0
LongAdder longAdder = new LongAdder();
// 1. 累加(常用,和AtomicInteger的addAndGet类似)
longAdder.add(5);
// 2. 自增(等价于add(1))
longAdder.increment();
// 3. 自减(等价于add(-1))
longAdder.decrement();
// 4. 获取最终结果
System.out.println("当前累加值:" + longAdder.sum()); // 5
// 高并发测试(10个线程,每个线程累加10000次)
int threadCount = 10;
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
longAdder.add(1);
}
});
}
// 启动所有线程
for (Thread thread : threads) {
thread.start();
}
// 等待所有线程执行完毕
for (Thread thread : threads) {
thread.join();
}
// 输出最终结果(预期10*10000=100000,无线程安全问题)
System.out.println("高并发累加后的值:" + longAdder.sum()); // 100000
}
}
3.3 LongAdder执行流程(mermaid图拆解)
是
否
是
否
初始化LongAdder,base=0,cells=null
线程调用add方法
低并发?
CAS修改base变量,base += x
通过哈希获取线程对应的Cell单元
Cell存在且CAS成功?
Cell.value += x
扩容cells数组或创建新Cell
获取结果时,sum=base + 所有Cell的值
四、ATOMICINTEGER VS LONGADDER(核心区别,面试必背)
这部分是面试高频考点,别死记硬背,结合场景理解,一张表搞懂所有区别:
| 对比维度 | AtomicInteger | LongAdder |
|---|---|---|
| 底层原理 | 基于CAS + 自旋,操作单个变量 | 基于分段累加(cells数组)+ CAS,多单元分工 |
| 并发性能 | 低并发优秀,高并发自旋冲突多,性能下降 | 高并发优秀,分段累加减少冲突,性能更稳定 |
| 内存开销 | 较小(仅维护一个value变量) | 较大(维护cells数组,高并发时数组会扩容) |
| 适用场景 | 低并发、需频繁获取精确值、原子操作多样(自增、自减、CAS) | 高并发、仅需累加/自增/自减,无需频繁获取精确值 |
| API丰富度 | 丰富(incrementAndGet、compareAndSet等) | 简单(主要是add、increment、sum等) |
补充一句大白话:如果你的场景是"偶尔修改,经常读值",用AtomicInteger;如果是"高并发频繁修改,偶尔读值",用LongAdder。比如:统计接口访问量(高并发累加),用LongAdder;统计线程执行状态(偶尔修改,经常判断),用AtomicInteger。
五、面试官追问环节(实战价值拉满,直接背)
整理了3个高频追问,附标准答案,面试时遇到直接答,不用慌!
追问1:AtomicInteger为什么能保证线程安全?底层依赖什么?
核心答案:AtomicInteger的线程安全,底层依赖「CAS机制 + volatile修饰」。
- volatile修饰value变量:保证多线程下变量的可见性,一个线程修改后,其他线程能立即看到最新值,避免脏读。
- CAS机制:基于CPU原子指令(cmpxchg),实现"比较并交换"的原子操作,无需加锁,避免线程阻塞。
- 自旋重试:当CAS修改失败时,会循环重试,直到修改成功,保证最终能完成原子操作。
追问2:LongAdder为什么比AtomicInteger在高并发下性能更好?
核心答案:两者的核心区别在于「冲突粒度不同」。
- AtomicInteger所有线程竞争同一个value变量,高并发时CAS自旋冲突严重,大量线程空转,占用CPU资源。
- LongAdder采用"分段累加",将一个变量拆分为多个Cell单元,多线程分散到不同Cell操作,减少CAS冲突,自旋次数大幅减少,高并发下性能更优。
- 补充:低并发时,LongAdder会先操作base变量,和AtomicInteger性能差不多,避免数组扩容带来的开销。
追问3:什么时候用AtomicInteger,什么时候用LongAdder?举个实际业务场景。
核心答案:根据「并发量」和「操作场景」选择,结合业务举例更加分。
- 用AtomicInteger的场景:低并发、需要精确控制原子操作(比如CAS比较并交换)、频繁获取变量精确值。
举例:线程池的核心线程数控制、并发场景下的开关状态(true/false)、低并发的计数器(比如后台任务执行次数)。 - 用LongAdder的场景:高并发、仅需要累加/自增/自减操作、不需要频繁获取精确值。
举例:网站访问量统计、接口请求次数统计、高并发任务的执行次数统计(比如秒杀活动的下单次数)。
六、总结
Java原子类是并发编程的"轻量级神器",AtomicInteger基于CAS自旋,适合低并发、多原子操作场景;LongAdder基于分段累加,适合高并发、纯累加场景,两者各有优势,核心是根据业务场景选择。

面试时,不仅要讲清两者的原理,还要能区分适用场景,再结合面试官追问的知识点,就能轻松拿下这个考点。记住:没有最好的工具,只有最适合的场景!