题目:如何实现无锁化编程?
难度:medium
标签:java/concurrency-practice, java/concurrency, general/knowledge
评分:⭐⭐⭐☆☆(3/5)
无锁编程的核心手段包括CAS,原子类,无共享状态等; CAS的核心是比较交换,以及原子性的单更新,通过比较预想值,当前值和目标值来实现对于属性更新时确保是从自己知道的改到自己目标的,但是存在ABA问题也就是当前的这个A和自己预期的A本质并不是一个,是其他线程修改后的结果; 原子类是通过cpu包装的原子操作进行对象的简单变更,但是只能应对简单的对象变更增加,并不能应对太复杂的操作; 无共享状态则是基于多线程没有交集,也就不会有线程安全问题,但是共享状态是很多业务所必需的,应用场景相对比较受限;
优点:CAS/原子类/无共享状态三条都答到,ABA 问题点出且解释准确,也意识到"只能处理单变量、简单操作"的边界。
不足:没给具体手段(AtomicInteger/AtomicReference/LongAdder/Unsafe/VarHandle);没说 ABA 的解法(版本号 AtomicStampedReference);没提高并发下的 自旋开销。
补充:无锁 = 乐观重试 + 原子指令;适用"单变量、低到中竞争",高竞争时 LongAdder 优于 AtomicLong,再高就走分段/有锁。
最后更新:2026-10-26 · 基于第 2 次作答
结论:无锁化 = CAS + 原子类 + 无共享状态 三条主线;核心是乐观重试 + 原子指令替代阻塞锁。 关键词:CAS cmpxchg · LongAdder 分段 · AtomicStampedReference 解 ABA 边界:只保单变量原子性;ABA 需版本号;高竞争自旋反而浪费 CPU
核心考点定位:无锁的手段清单 + 各自边界 + ABA 治理。
机制链(3 环):
cmpxchg 指令原子完成"读-比-写" → 失败重试而非阻塞AtomicStampedReference 加版本号解决 → 高竞争时自旋浪费 CPU → 改用 LongAdder 分段或锁证据锚点(去重 ≥10):
Unsafe.compareAndSwapInt:CAS 底层实现cmpxchg + lock:x86 指令,保证原子AtomicInteger / AtomicLong / AtomicReference:原子类 APIAtomicStampedReference:ABA 解法,版本号AtomicMarkableReference:ABA 简化解法,布尔标记LongAdder:分段 Cell 计数,高并发优于 AtomicLongVarHandle:Java 9+ 现代 CAS API(替代 Unsafe)ThreadLocal:无共享状态方案final + 构造期完成:不可变对象对比与权衡:
| 方案 | 原子性 | 竞争开销 | ABA 防护 | 适用 |
|---|---|---|---|---|
| synchronized / ReentrantLock | ✅ | 阻塞,高竞争 O(n) | 不需要 | 高竞争、复合操作 |
| CAS(AtomicXxx) | ✅(单变量) | 低竞争无自旋 | ❌ | 单变量低竞争 |
| AtomicStampedReference | ✅(单变量) | 低竞争无自旋 | ✅ | 单变量需 ABA 防护 |
| LongAdder | ✅(弱一致 sum) | 分段无自旋 | N/A | 高并发计数 |
| volatile(读写) | 不保证原子 | 内存屏障开销 | N/A | 可见性 |
常见误区:
AtomicReference 打包或直接加锁 → 为什么:两个变量的原子性无法用 CAS 单独保证适用边界:
AtomicStampedReference 加版本号;AtomicMarkableReference 加布尔标记sum() 是弱一致LockSupport.park)【结论】 无锁化编程本质是"用乐观重试 + 原子指令替代阻塞锁",三条主线:CAS + 原子类 + 无共享状态。
【三点】
第一,核心手段是 CAS 和原子类,CAS 靠 CPU 的 cmpxchg 加锁前缀保证原子,失败就自旋重试。它解决的是单变量的原子更新,复合操作要用 AtomicReference 打包。
第二,ABA 是 CAS 最大的坑——值 A→B→A 后 CAS 会误判为未变。用 AtomicStampedReference 加版本号解决,AtomicMarkableReference 用布尔标记。高并发计数场景用 LongAdder,它分段 Cell 分散热点,比 AtomicLong 快得多(sum() 是弱一致)。
第三,还有无共享状态的思路——不可变对象、ThreadLocal,从根上避开竞争。
【边界】 三点提醒:一是 CAS 只保单变量原子性,复合操作要 AtomicReference 或加锁;二是高竞争时 CAS 自旋反而浪费 CPU,不如 LongAdder 或退回到锁;三是 ABA 隐蔽,链表复用等场景常见,必须用版本号防护。
题目:如何实现无锁化编程?
难度:medium
标签:java/concurrency-practice, java/concurrency, general/knowledge, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
练习次数:2
无锁化编程方面有多种实现方式;首先可以规避共享对象的变更,没有多线程共享的资源也就没有锁的必要了;相关的有 ThreadLocal 的方案,和所有对象都在线程栈内部不做状态共享;另外就是通过 CAS 使用比较交换的方式,验证当前要变动更新的资源是我预期的资源,而没被其他线程污染,也可以实现无锁化的操作,不过多数这种只应对一个字段;另外就是原子命令,这个命令执行就一下不会有线程能插入进来,比如原子类的各种加减操作。
优点:
不足:
AtomicStampedReference 版本号解法)AtomicReference 打包)| 维度 | 2026-10-17(首次) | 2026-10-26(第 2 次) |
|---|---|---|
| 三条主线 | ✅ | ✅ |
| ABA 问题 | ⚠️ 提到 | ❌ 未提 |
| ABA 解法(AtomicStampedReference) | ❌ 未提 | ❌ 未提 |
| LongAdder | ❌ 未提 | ❌ 未提 |
| Unsafe / VarHandle | ❌ 未提 | ❌ 未提 |
| 复合操作 | ❌ 未提 | ⚠️ 只说"单字段" |
| 评分 | 3/5 | 3/5 |
结论:两次作答在 ABA 问题上连续失分——这是 Java 并发高频考点,必须专项订正。ABA 的记忆点:"AtomicStampedReference 版本号 / AtomicMarkableReference 布尔标记"。