无锁编程核心

CAS 和原子类是 Java 无锁编程的基石,理解其原理和使用场景是面试必考内容。

CAS 原理 ⭐⭐⭐⭐⭐

什么是 CAS

CAS(Compare-And-Swap) 是一种乐观锁机制,实现无锁并发控制。

三个操作数

  • V(内存地址):要更新的变量

  • E(预期值):期望的旧值

  • N(新值):要设置的新值

执行逻辑

// 伪代码
boolean compareAndSwap(V, E, N) {
    if (V == E) {
        V = N;
        return true;
    }
    return false;
}

CAS 底层实现

硬件层面

内存屏障

CAS 使用示例

线程安全的计数器

自旋锁实现

CAS vs 锁

特性
CAS
synchronized/Lock

阻塞

非阻塞(自旋)

阻塞(等待队列)

上下文切换

适用场景

低竞争、操作简单

高竞争、复杂操作

性能

高(低竞争时)

低(需加锁)

公平性

不公平

可配置

问题

ABA、自旋开销

死锁、性能差


ABA 问题 ⭐⭐⭐⭐⭐

问题描述

场景:线程 T1 读取值 A,准备 CAS 更新;线程 T2 将 A 改为 B,再改回 A;T1 的 CAS 成功,但中间状态已改变。

危害

  • 栈结构被破坏

  • 内存泄漏

  • 数据不一致

解决方案

1. 版本号(AtomicStampedReference)

完整示例

2. 标记位(AtomicMarkableReference)


Atomic 原子类 ⭐⭐⭐⭐⭐

基本类型原子类

AtomicInteger / AtomicLong / AtomicBoolean

性能对比

数组类型原子类

AtomicIntegerArray / AtomicLongArray / AtomicReferenceArray

引用类型原子类

AtomicReference

字段更新器

AtomicIntegerFieldUpdater / AtomicLongFieldUpdater / AtomicReferenceFieldUpdater

累加器(JDK 8+)

LongAdder / DoubleAdder

性能对比


无锁队列 ⭐⭐⭐⭐

ConcurrentLinkedQueue

特点

  • 基于 CAS 的无锁队列

  • FIFO 顺序

  • 线程安全

  • 适合高并发场景

实现原理

使用示例

ConcurrentLinkedDeque

双端队列,支持头尾两端操作:

队列对比

队列
阻塞
有界
实现
性能

ConcurrentLinkedQueue

❌ 非阻塞

❌ 无界

CAS 无锁

ArrayBlockingQueue

✅ 阻塞

✅ 有界

ReentrantLock

LinkedBlockingQueue

✅ 阻塞

✅ 可选

分段锁

LinkedTransferQueue

✅ 阻塞

❌ 无界

CAS 无锁


面试要点 ⭐⭐⭐⭐⭐

Q1: CAS 是什么?如何实现?

  • CAS = Compare-And-Swap,比较并交换

  • 三个操作数:内存地址 V、预期值 E、新值 N

  • 底层调用 CPU 指令(x86: LOCK CMPXCHG)

  • 具有 volatile 读写的内存语义

Q2: CAS 有什么问题?

  • ABA 问题:值从 A 改为 B 再改回 A,中间状态丢失

  • 自旋开销:高竞争时,大量线程自旋消耗 CPU

  • 只能保证一个变量:需要多个变量原子操作时无能为力

Q3: 如何解决 ABA 问题?

  • 使用 AtomicStampedReference:增加版本号

  • 使用 AtomicMarkableReference:增加标记位

  • 不可变对象:每次修改都创建新对象

Q4: AtomicInteger 和 synchronized 的区别?

  • AtomicInteger 基于 CAS,非阻塞,性能更高

  • synchronized 基于互斥锁,阻塞等待,有上下文切换

  • AtomicInteger 适合简单计数,synchronized 适合复杂临界区

Q5: LongAdder 为什么比 AtomicLong 快?

  • AtomicLong:所有线程竞争一个变量,CAS 冲突严重

  • LongAdder:分段累加,每个线程有自己的槽位,最后汇总

  • 高并发下,LongAdder 性能提升 5-10 倍

Q6: ConcurrentLinkedQueue 的实现原理?

  • 基于单向链表,使用 CAS 更新 head 和 tail

  • 允许 tail 滞后(延迟更新),减少 CAS 次数

  • 入队和出队都是无锁的,适合高并发

Q7: 什么时候使用 CAS,什么时候使用锁?

  • CAS 适用:低竞争、操作简单(计数、状态标志)

  • 锁适用:高竞争、复杂操作、需要公平性

Q8: volatile 和 AtomicInteger 的区别?

  • volatile 只保证可见性和有序性,不保证原子性

  • AtomicInteger 保证原子性(基于 CAS)

  • volatile int i; i++ 不是线程安全的

  • AtomicInteger i; i.incrementAndGet() 线程安全


参考资料

  1. 书籍推荐:《Java 并发编程的艺术》、《Java 并发编程实战》

  2. 论文:《Practical lock-freedom》- Keir Fraser

  3. 源码:java.util.concurrent.atomic 包

Last updated