AQS 是实现各种业务 Lock 的基础框架,例如ReentrantLock的实现底层就是使用 AQS。我们可以参考ReentrantLock来实现自己特定需求的 Lock 逻辑。
AQS 框架本身依赖两个强有力的工具,Unsafe 和 LockSupport,可以说是左膀右臂。
因此理解 AQS 中是如何使用 Unsafe 和 LockSupport 的,有助于我们理解各类 Lock 的底层实现原理,也可以在面试时系统地回答 AQS 相关的问题。
AQS 中对 Unsafe 的使用,主要是 CAS 操作。 例如:
/**
* CAS head field. Used only by enq.
* 入队时 CAS 修改队列 head
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
* 入队时 CAS 修改队列 tail
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
AQS 里对 LockSupport 的使用,主要是用于将入队后的线程 park,以及unpark 队列中某个线程。
// 未获取到锁的线程被包装成 Node,然后加入队列中
// 进入队列的 Node 如果处在 head,则尝试抢锁一次
// 若不在 head 位置或在 head 抢锁不成功,则进入 park
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); // 返回前一个节点
if (p == head && tryAcquire(arg)) { // 前一个节点是head,则尝试抢锁一次
setHead(node); // 抢锁成功,自己变为 head
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 若 Lock 的逻辑是抢锁失败后 park,则这里让线程进入 park
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 入队的线程 park
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
LockSupport的两个核心方法park()和unpark(Thread thread),内部都是通过 Unsafe 的方法来实现。
// LockSupport 的 park 方法直接调用 Unsafe#park
public static void park() {
UNSAFE.park(false, 0L);
}
// LockSupport 的 unpark 方法直接调用 Unsafe#unpark
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。且在 park unpark 过程中,保护了一个_counter的变量。
当调用park时,先尝试能否直接拿到“许可”,即判断_counter>0时,如果成功,则把_counter设置为0,并返回。否则进入等待。
当调用 unpark 时,直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程。
源码讲解可参考LockSupport(park/unpark)源码分析
【参考】