java无锁编程核心是用原子类和cas避免阻塞锁以提升并发性能,但需应对aba问题、内存可见性、循环开销等风险,典型案例如atomicreference实现无锁栈。

Java 中无锁编程的核心是借助 原子类(如 AtomicInteger、AtomicReference)和 CAS(Compare-And-Swap)操作,避免使用 synchronized 或 ReentrantLock 等阻塞式锁,从而提升高并发下的吞吐量与响应性。但无锁 ≠ 无设计 —— 它需要更严谨的状态建模、线程安全的变更逻辑,以及对 ABA 问题、内存可见性、循环开销等风险的主动应对。
用 AtomicReference 构建无锁栈(Lock-Free Stack)
这是经典入门案例:用单链表 + CAS 实现线程安全的 push/pop,不依赖锁。
- 核心思路:栈顶 node 用 AtomicReference
持有;每次 push 或 pop 都基于当前 top 值做 CAS 更新 - push 操作:构造新节点 → 读取当前 top → 新节点 .next = top → CAS 将 top 从旧值更新为新节点
- pop 操作:读取当前 top → 若非空,获取 next → CAS 将 top 从旧 top 更新为 next;失败则重试(典型自旋)
- 注意:需用 AtomicReferenceFieldUpdater 或内部 volatile 字段保证 next 的可见性;pop 中要处理空栈(top == null)边界
规避 ABA 问题:用 AtomicStampedReference 或版本号
CAS 只比较值是否相等,无法区分“值相同但中间被改过又改回”的情况(即 ABA)。例如:栈顶从 A → B → A,CAS 会误认为未变而成功,导致逻辑错误。
- 推荐方案:用 AtomicStampedReference,将引用 + 时间戳(或版本号)打包成 pair,CAS 同时校验二者
- 简单替代:在业务对象中显式添加 int version 字段,用 AtomicReference
> 手动管理版本递增 - 注意:版本号不能溢出(可用 long 或带 wrap-around 检查),且每次修改必须严格递增
避免无限自旋:加入退避(backoff)与终止条件
纯 CAS 失败后重试,若竞争激烈可能持续占用 CPU,甚至饿死某些线程。
- 轻量级退避:失败后 Thread.onSpinWait()(JDK9+),提示 CPU 当前为自旋等待
- 指数退避:记录失败次数,按 1, 2, 4, ... 纳秒级 sleep 或 yield,防止恶性竞争
- 设定最大重试次数:超过则抛异常或降级为阻塞逻辑(如 fallback 到 ConcurrentLinkedQueue)
- 关键点:无锁结构仍需明确“操作一定能在有限步内完成”,否则不是 lock-free,而是 wait-free 的弱化形式
慎用无锁:优先考虑 java.util.concurrent 已有实现
绝大多数场景无需手写无锁结构 —— JDK 提供了高度优化的无锁/低锁组件:
- ConcurrentLinkedQueue / ConcurrentLinkedDeque:基于无锁单/双向链表,适合高并发队列
- LongAdder / DoubleAdder:分段累加 + CAS,比 AtomicInteger 在高争用下性能更好
- VarHandle(JDK9+)与 Unsafe:提供更底层、可定制的原子访问能力,但需谨慎使用
- 提醒:无锁调试困难、逻辑复杂、易出隐蔽 bug;除非压测证明锁是瓶颈,否则不建议替换 ConcurrentHashMap 等成熟实现
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











