原子性指单个操作不可中断,如AtomicInteger.incrementAndGet()通过CAS保证“读-改-写”原子执行;它无锁高效,但仅保障单变量原子性,多字段联动仍需synchronized或Lock。

原子性不是“绝对安全”,而是“单个操作不可中断”
原子性在Java并发中,指的是一个操作(比如 incrementAndGet())从开始到结束,不会被线程调度打断——它要么全部执行完,要么根本没开始。这不等于整个业务逻辑线程安全。例如 i++ 看似简单,实际是“读-改-写”三步,中间可能被其他线程插队,所以它**不是原子操作**;而 AtomicInteger.incrementAndGet() 底层用CAS+volatile保证这三步合为一个不可分割的动作。
为什么用 AtomicInteger 而不用 synchronized?
因为它是无锁的:不阻塞线程、没有上下文切换开销,适合高并发计数场景(如请求统计、ID生成)。但要注意:
-
synchronized是悲观锁,一进就锁住临界区;AtomicInteger是乐观锁,靠底层CPU的CAS指令反复尝试更新,失败就重试 - 当竞争非常激烈(比如上千线程同时争抢一个
AtomicInteger),CAS失败率升高,可能反而比LongAdder慢得多 - 它只保单变量原子性,多个字段联动(如“余额减1且状态变已扣款”)仍需
synchronized或Lock
compareAndSet() 是原子类的底层引擎,也是最容易误用的地方
这个方法签名是 boolean compareAndSet(int expect, int update),意思是:“如果当前值等于 expect,才把它设成 update,否则不动并返回 false”。它常被用来实现自旋逻辑,但新手常犯两个错:
- 忽略返回值,直接调用后就认为更新成功 —— 实际上失败时啥也没发生
- 把
expect写成固定值(比如硬编码0),而不是动态读取的当前值,导致 CAS 总是失败 - 在循环里用
compareAndSet()时,没配合get()重读最新值,陷入死循环或跳过更新
int current;
do {
current = counter.get();
} while (!counter.compareAndSet(current, current + 1));
别拿 AtomicReference 当“万能线程安全包装器”
AtomicReference 只保证“引用本身”的更新是原子的,不保证它指向的对象内部状态线程安全。比如你用它存一个 ArrayList,多个线程调用 list.add() 依然会出问题——因为 add() 不是原子操作,而且 ArrayList 本身非线程安全。
立即学习“Java免费学习笔记(深入)”;
- 如果对象有可变状态,要么让对象自己线程安全(如用
CopyOnWriteArrayList),要么在业务层加同步 - 需要防止 ABA 问题(比如值从 A→B→A)时,得换用
AtomicStampedReference或AtomicMarkableReference,不能硬扛 -
lazySet()虽然性能略高,但只保证写不重排序、不保证立即对其他线程可见,慎用于关键状态位











