Java中CAS是构建无锁同步组件的核心机制,AQS子类通过volatile声明state字段并结合AtomicIntegerFieldUpdater或VarHandle进行原子更新,以安全实现状态标志位控制、避免加锁开销。

Java 中 CAS(Compare-And-Swap)是构建无锁同步组件的核心机制,尤其在自定义 AQS(AbstractQueuedSynchronizer)子类中,通过 Unsafe.compareAndSwapInt 或更推荐的 AtomicIntegerFieldUpdater / VarHandle 对 state 字段进行原子更新,可安全地变更状态标志位(如表示锁是否被占用、条件队列是否等待等),避免加锁开销。
用 volatile + CAS 保证 state 的可见性与原子性
AQS 的 state 字段必须声明为 volatile,确保多线程间状态变更的可见性;但 volatile 不提供原子读-改-写能力,因此所有状态变更(如加锁、释放、唤醒)都需基于 CAS 实现:
- 直接使用
Unsafe(不推荐,JDK 内部 API,易出错且可能被移除) - 优先使用
AtomicIntegerFieldUpdater<YourSyncClass>:类型安全、反射开销低、兼容老版本 JDK - JDK 9+ 推荐使用
VarHandle:标准化、性能好、支持更多内存语义(如weakCompareAndSetPlain)
用 CAS 实现状态标志位的位运算控制
若 state 不仅表示“是否占用”,还需编码多个标志(如:bit0=locked, bit1=hasWaiters, bit2=timeoutEnabled),可结合位操作与 CAS 循环实现无锁状态切换:
例如,仅当未加锁(state == 0)时设置 locked 标志(bit0):
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
int expect, update;
do {
expect = state.get();
if ((expect & 1) != 0) return false; // 已锁定,失败
update = expect | 1;
} while (!state.compareAndSet(expect, update));
return true;
关键点:
- CAS 必须在循环中重试(loop until success),因为并发修改可能导致 compare 失败
- 位判断和构造新值必须基于同一份
expect,避免中间态污染 - 避免对
state做非原子的复合操作(如state++),除非明确只需单标志且值单调递增
在 AQS 子类中正确集成 CAS 状态变更
AQS 本身已封装了 compareAndSetState(int expect, int update)(底层用 Unsafe),子类应优先复用它,而非绕过 AQS 自行操作 state 字段:
- 重写
tryAcquire(int arg)时,用compareAndSetState(0, 1)尝试独占获取 - 重写
tryRelease(int arg)时,用setState(0)(无需 CAS,因释放者唯一)或带校验的 CAS(如确保当前 state == 1) - 若需多状态(如共享锁计数),
tryAcquireShared可用 CAS 更新 state(如 state += 1),并配合循环处理竞争
注意:setState/getState 是 volatile 读写,适合单线程写+多线程读场景;涉及读-改-写,必须用 compareAndSetState 或其变体。
避免 ABA 问题与过度自旋的实用建议
纯整型 state 的 CAS 在极少数场景下存在 ABA 风险(如 state 从 0→1→0,CAS 误认为未变),但对 AQS 的典型用途(锁状态、计数器)影响极小,一般无需引入 AtomicStampedReference:
- 真正需要版本号的场景(如带超时/中断的复杂状态机),可将 state 拆分为 long 类型,高 32 位存版本号,低 32 位存业务状态,再用
compareAndSwapLong - 若 CAS 循环频繁失败(如高争用),考虑退化策略:短暂 yield / park,或结合自适应自旋(AQS 内部已对 head 后首个节点做简单自旋优化)
- 调试时可用
getQueueLength()或 JUC 工具类观察等待线程数,验证无锁逻辑是否导致饥饿

















