Java中不推荐在无锁算法中结合自旋锁实现链表插入,因其违背lock-free原则;正确做法是仅用AtomicReference的CAS乐观重试完成头插,必要时用AtomicStampedReference解决ABA问题。

Java 中不推荐在无锁算法中“结合自旋锁”来实现链表插入——因为这本质上违背了无锁(lock-free)的设计原则。自旋锁是基于 synchronized 或 Lock 的阻塞/忙等待机制,而无锁算法依赖的是原子操作(如 AtomicReference.compareAndSet)和乐观重试,不依赖锁,也不应混入自旋等待逻辑。
为什么不能“用 AtomicReference + 自旋锁”做无锁链表
自旋锁(比如用 while(!locked) { Thread.onSpinWait(); } 手动实现)仍属于“锁”的语义:它试图独占临界资源,可能引发线程饥饿、CPU 空转、ABA 问题加剧,且无法保证 lock-free 性(即任意线程挂起时,其他线程仍能前进)。真正的无锁链表插入必须全程靠 CAS 乐观更新,失败则重试,不阻塞、不等待锁。
正确的做法:用 AtomicReference 实现无锁单向链表插入
核心是维护一个原子引用指向头节点,并在插入时用 CAS 原子地更新头节点或中间节点的 next 字段。以头插法为例:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义节点:
static class Node { final int value; volatile Node next; Node(int v) { value = v; } } - 头节点用
AtomicReference<Node> head = new AtomicReference<>(); - 插入逻辑:
Node newNode = new Node(x);
Node oldHead;
do {
oldHead = head.get();
newNode.next = oldHead;
} while (!head.compareAndSet(oldHead, newNode));
这就是标准的无锁头插——没有锁、没有自旋锁,只有 CAS 重试。
处理并发修改导致的 ABA 问题
单纯用 AtomicReference 在多线程反复修改同一地址时可能出错(例如节点被弹出又压入,CAS 误判为未变)。解决方案:
立即学习“Java免费学习笔记(深入)”;
- 升级为
AtomicStampedReference,给引用带上版本戳,每次修改 stamp +1 - 或使用
AtomicMarkableReference标记节点是否已删除(适合带删除的链表) - 更彻底的方式:用
Unsafe+ 循环数组或借助java.util.concurrent.ConcurrentLinkedQueue的成熟实现(它正是基于无锁单向链表)
插入到指定位置(非头插)的注意事项
若需尾插或中间插入,需同时确保前驱节点未被修改、其 next 字段仍指向预期节点。典型模式是“双 CAS”或“帮助式”策略:
- 先读取前驱
pred和其next(即后继next),构造新节点并设置newNode.next = next - 再用 CAS 尝试将
pred.next从next更新为newNode - 失败则重新遍历定位前驱——整个过程仍是纯 CAS + 重试,无需任何锁结构

















