原子变量:无锁更新
使用 AtomicInteger、AtomicLong 和 AtomicReference,在无锁情况下实现线程安全的计数器
原子变量:无锁更新 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
非原子操作的问题
简单的递增操作 count++ 不具备线程安全性——它包含三个操作:读取、add、写入。并发线程可能交错执行这些操作,从而导致更新丢失。原子变量无需使用锁即可解决此问题。
// UNSAFE:
int count = 0;
// Thread A reads count=0, Thread B reads count=0
// Both write 1 — second update lost!
count++; // NOT thread-safe
// SAFE:
import java.util.concurrent.atomic.*;
AtomicInteger atomicCount = new AtomicInteger(0);
atomicCount.incrementAndGet(); // atomic, lock-freeAtomicInteger 基础
AtomicInteger 使用 CPU 的比较并交换(CAS)指令提供有整数操作,这些操作能够保证原子性——无需使用锁。
AtomicInteger counter = new AtomicInteger(0);
counter.set(10); // set to 10
System.out.println(counter.get()); // 10
int prev = counter.getAndIncrement(); // returns 10, sets to 11
int curr = counter.incrementAndGet(); // sets to 12, returns 12
counter.addAndGet(5); // adds 5, returns 17
counter.decrementAndGet(); // subtracts 1, returns 16compareAndSet:CAS 操作
compareAndSet(expected, update) 仅当当前值等于预期值时才会以原子方式更新该值。成功时返回真。这是所有无锁算法的基础。
AtomicInteger ai = new AtomicInteger(5);
boolean updated = ai.compareAndSet(5, 10); // current==5? set to 10
System.out.println(updated); // true
System.out.println(ai.get()); // 10
boolean failed = ai.compareAndSet(5, 20); // current==10, not 5 → fails
System.out.println(failed); // false
System.out.println(ai.get()); // still 10使用 AtomicLong 作为计数器
AtomicLong 是对应的 64 位类型,常用于序列生成器和命中计数器:
AtomicLong seq = new AtomicLong(0);
// Thread-safe sequence generator:
long nextId() {
return seq.incrementAndGet();
}
System.out.println(nextId()); // 1
System.out.println(nextId()); // 2
System.out.println(nextId()); // 3AtomicBoolean
线程安全的布尔标志。常用于一次性初始化或关闭信号:
AtomicBoolean initialized = new AtomicBoolean(false);
void initOnce() {
// compareAndSet: only first caller succeeds
if (initialized.compareAndSet(false, true)) {
System.out.println("Initializing...");
// perform expensive init
} else {
System.out.println("Already initialized");
}
}AtomicReference
AtomicReference<V> 提供对对象引用的原子更新。它用于实现无锁数据结构:
import java.util.concurrent.atomic.*;
record Config(String host, int port) {}
AtomicReference<Config> configRef =
new AtomicReference<>(new Config("localhost", 8080));
// Hot-swap the configuration atomically:
Config oldConfig = configRef.get();
Config newConfig = new Config("prod.example.com", 443);
boolean swapped = configRef.compareAndSet(oldConfig, newConfig);
System.out.println(swapped); // true
System.out.println(configRef.get()); // Config[host=prod.example.com, port=443]updateAndGet / getAndUpdate
使用内部的 CAS 重试循环,以原子方式应用函数:
AtomicInteger value = new AtomicInteger(10);
// Apply function atomically: square the value
int newVal = value.updateAndGet(x -> x * x);
System.out.println(newVal); // 100
// Get old, then apply:
int old = value.getAndUpdate(x -> x + 1);
System.out.println(old); // 100
System.out.println(value.get()); // 101accumulateAndGet
使用函数将当前值与给定值合并:
AtomicInteger max = new AtomicInteger(0);
// Keep running maximum:
max.accumulateAndGet(42, Math::max); // max(0, 42) = 42
max.accumulateAndGet(15, Math::max); // max(42, 15) = 42
max.accumulateAndGet(99, Math::max); // max(42, 99) = 99
System.out.println(max.get()); // 99用于高竞争计数器的 LongAdder
在线程竞争激烈时,AtomicLong 的 CAS 重试可能会降低性能。LongAdder 使用多个单元来减少竞争:
import java.util.concurrent.atomic.*;
LongAdder adder = new LongAdder();
// Multiple threads can call add() with minimal contention:
adder.increment();
adder.add(5);
adder.increment();
System.out.println(adder.sum()); // 7
// Note: sum() is NOT atomic with add() — use only when no concurrent adds原子数组
AtomicIntegerArray / AtomicLongArray 为单个数组元素提供原子操作:
AtomicIntegerArray arr = new AtomicIntegerArray(5);
arr.set(0, 10);
arr.incrementAndGet(0);
arr.compareAndSet(2, 0, 42); // index 2: if 0, set 42
System.out.println(arr.get(0)); // 11
System.out.println(arr.get(2)); // 42何时使用原子变量而不是锁
请在以下情况下使用原子变量:
- 更新单个变量(计数器、标志或引用)
- 竞争较低的场景
- 无锁性能很重要
请在以下情况下使用锁:
- 必须以原子方式更新多个变量
- 需要复杂的条件逻辑
- 竞争非常激烈(CAS 重试会降低性能)
快速检查
compareAndSet(expected, update) 能保证什么?
回顾:原子变量
关键要点:
- 原子类型使用 CPU 的 CAS 指令——无锁且线程安全
- AtomicInteger/Long/Boolean/Reference 用于单变量原子性
- compareAndSet 是无锁算法的基础
- updateAndGet/accumulateAndGet 用于函数式更新
- LongAdder 适用于高竞争的计数器场景
常见问题解答
「原子变量:无锁更新」课时是免费的吗?
是的 — 「原子变量:无锁更新」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「原子变量:无锁更新」这节课中我会学到什么?
使用 AtomicInteger、AtomicLong 和 AtomicReference,在无锁情况下实现线程安全的计数器 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「原子变量:无锁更新」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。