无锁编程在Java中可靠与否取决于是否正确使用Unsafe、VarHandle或Atomic类族并规避ABA等问题;多数场景应优先选用AtomicInteger而非手写CAS循环,因其封装了正确的内存屏障语义且经JDK长期验证。

无锁编程在 Java 中不是“不可靠”,但它的可靠性完全取决于你是否正确使用了 Unsafe、VarHandle 或 Atomic 类族,以及是否规避了 ABA、内存重排序、伪共享等底层陷阱。它不提供自动的线程安全语义,而是把责任交给了开发者。
什么时候该用 AtomicInteger 而不是自己写 CAS 循环
绝大多数场景下,直接用 AtomicInteger 就够了——它封装了 Unsafe.compareAndSet 的正确内存屏障语义,且经过 JDK 长期验证。自己手写 CAS 循环容易漏掉 volatile 读写顺序或误用 getPlain 等弱一致性方法。
- 计数器、状态标志位、简单状态机转换(如从
INIT→RUNNING)优先用AtomicInteger/AtomicReference - 需要自定义失败重试逻辑(比如带退避的 CAS)才考虑裸调
VarHandle.compareAndSet -
AtomicStampedReference是为解决 ABA 问题设计的,但代价是额外字段和更多内存开销,别为了“听起来更安全”而滥用
Unsafe 直接操作的风险点在哪
Unsafe 绕过了 JVM 的类型安全与内存模型约束,一旦出错就是 Segmentation fault 或静默数据损坏,而不是抛异常。JDK 9+ 已将它标记为 @Deprecated(forRemoval = true),且默认模块系统禁止反射访问。
- 字段偏移量计算依赖类布局,开启
-XX:+UseCompressedOops或不同 JVM 版本可能导致偏移错位 -
putOrdered不保证后续读可见,若误用于关键同步点(如发布对象引用),可能引发空指针 - 没有 GC 根管理能力:用
allocateInstance创建的对象不会触发构造器,字段为默认值,且可能被提前回收(若无强引用)
无锁队列(如 MpscArrayQueue)为什么比 ConcurrentLinkedQueue 快
核心差异在于内存访问模式:MpscArrayQueue(来自 JCTools)用单生产者 + 多消费者模型,避免了多生产者间的 CAS 竞争;而 ConcurrentLinkedQueue 是无界链表,每个入队都要分配新节点、更新多个 volatile 字段,还受 GC 压力影响。
立即学习“Java免费学习笔记(深入)”;
- 数组结构天然支持缓存行局部性,
MpscArrayQueue通过pad字段隔离头尾指针,缓解伪共享 - 它依赖
lazySet更新 tail,省去 full barrier 开销;但这也意味着消费者看到新元素有微小延迟(通常可接受) - 注意容量固定——溢出时行为取决于实现(如丢弃或阻塞),不能当通用替代品
// 示例:MpscArrayQueue 的典型用法(非 JDK 自带,需引入 jctools) import org.jctools.queues.MpscArrayQueue; MpscArrayQueuequeue = new MpscArrayQueue<>(1024); queue.offer("task-1"); // 无锁、快速 String task = queue.poll(); // 可能返回 null,需判空
真正难的不是写出一个无锁结构,而是证明它在线程交错下的正确性。哪怕只改一行 CAS 条件或换一个内存序,都可能让整个算法在某个 CPU 架构上失效。生产环境优先选成熟库(如 JCTools、Agrona),而不是抄论文伪代码。











