1. 什么是 AtomicInteger
?
AtomicInteger
是 Java 提供的一个 线程安全 的整数包装类,位于 java.util.concurrent.atomic
包下。
它允许你在 不使用 synchronized
锁 的情况下,对整数进行原子操作(如自增、自减、加法、比较交换等),从而避免竞态条件(race condition)。
import java.util.concurrent.atomic.AtomicInteger; AtomicInteger counter = new AtomicInteger(0);
2. 常用方法详解
2.1. get()
和 set(int newValue)
get()
:获取当前值set(int newValue)
:设置新值(原子操作)
AtomicInteger ai = new AtomicInteger(5); System.out.println(ai.get()); // 输出: 5 ai.set(10); System.out.println(ai.get()); // 输出: 10
等价于 volatile 变量的读写,保证可见性。
2.2. incrementAndGet()
和 getAndIncrement()
这两个是最常用的 自增 方法。
方法 | 含义 | 返回值 |
| 先 +1,再返回 | 新值 |
| 先返回,再 +1 | 旧值 |
AtomicInteger ai = new AtomicInteger(5); int a = ai.incrementAndGet(); // 先加1 → 6,再返回 System.out.println(a); // 6 System.out.println(ai.get()); // 6 int b = ai.getAndIncrement(); // 先返回6,再加1 → 7 System.out.println(b); // 6 System.out.println(ai.get()); // 7
类比:
++i
→incrementAndGet()
i++
→getAndIncrement()
推荐用于计数器、ID 生成等场景。
2.3. decrementAndGet()
和 getAndDecrement()
同理,这是 自减 操作。
AtomicInteger ai = new AtomicInteger(5); int a = ai.decrementAndGet(); // 4 int b = ai.getAndDecrement(); // 先返回4,再变3
2.4. addAndGet(int delta)
和 getAndAdd(int delta)
加一个指定值。
AtomicInteger ai = new AtomicInteger(10); int a = ai.addAndGet(5); // 15 int b = ai.getAndAdd(3); // 返回15,然后变成18
适用于需要加减任意数值的场景。
2.5. compareAndSet(int expect, int update)
这是最核心的方法!基于 CAS(Compare-And-Swap) 实现。
如果当前值等于 expect
,就把它更新为 update
,并返回 true
;否则返回 false
。
AtomicInteger ai = new AtomicInteger(10); boolean success = ai.compareAndSet(10, 20); System.out.println(success); // true,值变为20 boolean failed = ai.compareAndSet(10, 30); System.out.println(failed); // false,因为当前值是20,不是10
应用:实现无锁算法、状态机、乐观锁等。
2.6. getAndUpdate(IntUnaryOperator updateFunction)
Java 8 引入的新方法:以函数式方式更新值。
AtomicInteger ai = new AtomicInteger(10); // 当前值乘以2 ai.getAndUpdate(x -> x * 2); System.out.println(ai.get()); // 20
等价于:
int oldValue = ai.get(); while (!ai.compareAndSet(oldValue, oldValue * 2)) { oldValue = ai.get(); }
但更简洁!
2.7. getAndAccumulate(int x, IntBinaryOperator accumulator)
使用二元操作符累积值。
AtomicInteger ai = new AtomicInteger(5); // 相当于 ai += 3 ai.getAndAccumulate(3, (current, input) -> current + input); System.out.println(ai.get()); // 8
也可以做其他操作,比如取最大值:
ai.getAndAccumulate(10, Integer::max); // 取当前值和10的最大值
2.8. accumulateAndGet(...)
和 updateAndGet(...)
与上面类似,但 先更新,再返回。
AtomicInteger ai = new AtomicInteger(5); int result = ai.accumulateAndGet(3, (a, b) -> a + b); // 先计算 5+3=8,再设置,最后返回8 System.out.println(result); // 8
3. 底层原理:CAS + volatile
AtomicInteger
的线程安全性来自于两个核心技术:
技术 | 作用 |
CAS(Compare-And-Swap) | 保证“比较并交换”是一个原子操作,由 CPU 指令支持(如 x86 的 |
volatile | 保证变量的修改对所有线程立即可见(内存可见性) |
它内部结构简化如下:
private volatile int value; private static final long valueOffset = unsafe.objectFieldOffset(...); public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
value
是volatile
的,保证可见性unsafe
调用底层 CAS 指令,保证原子性
4. 使用场景
场景 | 示例 |
计数器 | 页面访问量、请求计数 |
ID 生成器 | 内存中生成唯一 ID(如你之前的 |
状态标志 |
|
限流/信号量 | 简单的并发控制 |
无锁算法 | 构建高性能数据结构 |
5. 注意事项
5.1. 不要依赖“中间状态”
虽然单个操作是原子的,但多个操作组合不是。
错误写法:
if (ai.get() < 10) { ai.incrementAndGet(); // ❌ 在 get 和 increment 之间可能被其他线程修改 }
正确做法:使用 compareAndSet
或同步块。
5.2. 高竞争下可能自旋过度
在大量线程竞争时,CAS 失败会重试(自旋),消耗 CPU。
建议:
低并发:用
AtomicInteger
高并发:考虑
LongAdder
5.3. 初始值很重要
AtomicInteger counter = new AtomicInteger(0); // 清晰表达意图
6. 对比:AtomicInteger
vs synchronized
vs LongAdder
方式 | 性能 | 适用场景 |
| 较低,阻塞 | 复杂逻辑,临界区大 |
| 高,无锁 | 单一变量原子操作 |
| 极高,分段累加 | 高并发计数(如监控系统) |
LongAdder
是 AtomicLong
/AtomicInteger
的升级版,用于高并发累加场景。
LongAdder adder = new LongAdder(); adder.increment(); long sum = adder.sum(); // 获取总和
7. 完整示例:线程安全的计数器
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AtomicCounter { private static AtomicInteger counter = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 1000; i++) { executor.submit(() -> { counter.incrementAndGet(); }); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); System.out.println("最终计数: " + counter.get()); // 一定是 1000 } }
即使 10 个线程并发操作,结果也准确无误。
8. 总结
方法 | 用途 |
| 读写值 |
| 先加后取(推荐) |
| 先取后加 |
| CAS 核心,实现乐观锁 |
| 函数式更新 |
| 加指定值 |
0条评论
点击登录参与评论