acquire(int arg)是aqs独占模式获取锁的核心方法,先tryacquire尝试cas抢占,失败则addwaiter入队、acquirequeued自旋等待并可能挂起,全程不响应中断。

Java AQS(AbstractQueuedSynchronizer)中,独占模式下获取锁的核心方法是 acquire(int arg),它不响应中断、不超时,是 ReentrantLock.lock() 等 API 的底层支撑。它的流程本质是:先尝试快速抢占(CAS),失败则入队等待、挂起,被唤醒后重试。
一、acquire 的主干逻辑很简洁
源码位于 java.util.concurrent.locks.AbstractQueuedSynchronizer:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这行代码包含三个关键动作,按顺序执行:
-
tryAcquire(arg):由子类实现(如
ReentrantLock.Sync),用 CAS 尝试修改 state。成功则直接获得锁,不进队列;失败则继续下一步。 - addWaiter(Node.EXCLUSIVE):将当前线程封装为独占模式的 Node,插入同步队列尾部(带 CAS 保证线程安全)。
-
acquireQueued(node, arg):让该节点在队列中自旋等待。若前驱是 head 且再次 tryAcquire 成功,则成为新 head;否则调用
shouldParkAfterFailedAcquire设置前驱 waitStatus 为 SIGNAL,并调用park挂起线程。
二、addWaiter:线程如何安全入队
这个方法处理两个常见场景:
- 队列已存在:用 CAS 将新节点设为 tail;失败则进入循环重试。
- 队列为空(tail == null):先调用
enq(node)初始化 head 和 tail(head 是哑结点,不关联任何线程)。
注意:addWaiter 返回的是新创建的 Node,它已被链入队列尾部,但此时还没开始等待。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
三、acquireQueued:真正“排队等锁”的核心
该方法在一个 for 循环中反复检查是否能获取锁,逻辑分三步:
- 检查前驱节点是否为 head:只有 head 后继才有资格竞争锁(FIFO 原则)。
- 再次调用
tryAcquire(arg):避免唤醒开销,也支持“唤醒即得锁”优化。 - 若仍失败,则调用
shouldParkAfterFailedAcquire(p, node)判断是否可挂起:- 前驱 waitStatus == -1(SIGNAL)→ 可安全 park;
- waitStatus > 0(CANCELLED)→ 跳过所有取消节点;
- 其他情况 → 先将前驱设为 SIGNAL,再重试判断。
一旦 park,线程会阻塞,直到被前驱节点在释放锁时调用 unparkSuccessor 唤醒。
四、补充:为什么 acquire 不响应中断
整个流程中没有检查 Thread.interrupted(),也不会抛出 InterruptedException。如果线程在 park 期间被中断,acquireQueued 会返回 true,最终触发 selfInterrupt() —— 即补上一次中断标记(不是抛异常)。这是为了保持语义一致性:lock() 必须拿到锁才返回,中断只是“通知”,不改变加锁结果。
若需响应中断,应使用 acquireInterruptibly(arg),它在 park 前后都检查中断状态并抛出异常。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










