1.简介
AbstractQueuedSynchronizer,简称AQS,采用模板方法设计模式,是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。锁是面向使用者,它定义了使用者与锁交互的接口,隐藏了实现细节;同步器是面向锁的实现者,它简化了锁的实现方式,屏蔽了同步状态的管理,线程的排队,等待和唤醒等底层操作。AQS的核心是一个同步队列和一个volatile修饰的状态state,支持独占式操作和共享式操作,比较常见的ReentrantLock使用的是独占式,Semaphore、CountDownLatch等使用的是共享式。
2.AQS同步队列
AQS内部维护了一个CLH队列,用于管理同步状态及线程排队:
static final class Node {
// 用于标记节点是共享模式
static final Node SHARED = new Node();
// 用于标记节点是独占模式
static final Node EXCLUSIVE = null;
// waitStatus为CANCELLED状态,表示当前的线程被取消、被打断或者获取超时了
static final int CANCELLED = 1;
// waitStatus为SIGNAL状态,表示当前节点的后继节点已经(或者很快会)通过park阻塞,因此当前节点释放或者取消的时候必须unpark它的后继节点
static final int SIGNAL = -1;
// waitStatus为CONDITION状态,表示当前节点在 condition 队列中等待,直到状态在某个时间节点被转化为0前,它都不会最为同步队列的一个节点被使用
static final int CONDITION = -2;
// waitStatus为CONDITION状态,表示当前场景下后续的 acquireShared 能够得以执行,这是设置(仅适用于头节点)doReleaseShared保证继续传播
static final int PROPAGATE = -3;
// 节点等待状态
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 被节点包装的线程
volatile Thread thread;
// 情境一:用于标记节点是独占模式还是共享模式
// 情景二:用于指向条件队列的下一个节点
Node nextWaiter;
// 判断节点是独占模式还是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 获取节点的前驱节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 无参构造器,用于创建哨兵节点或者共享模式标记节点
Node() {
}
// 用于同步队列创建新节点
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
// 用于条件队列创建新节点
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
3.独占式获取锁
3.1 不响应线程中断
3.1.1 流程图
3.1.2 JDK源码
public final void acquire(int arg) {
// 步骤1:tryAcquire,尝试获取锁
// 步骤2:addWaiter,把当前线程包装成Node,添加到同步队列队尾
// 步骤3:acquireQueued,排队获取锁
// 步骤4:selfInterrupt,判断排队获取锁过程中线程是否被中断,如果被中断,就再次中断当前线程
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 步骤1:尝试获取锁,模板方法,具体实现由子类完成
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 步骤2:把当前线程包装成独占模式的Node,添加到同步队列队尾
private Node addWaiter(Node mode) {
// 创建新节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
//尾节点不为空,说明同步队列已经初始化过,尝试CAS添加新节点到同步队列队尾,成功则返回新节点,失败则进入enq。
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 同步队列未初始化,或者上一步CAS操作失败,通过自旋添加新节点到同步队列队尾
enq(node);
// 返回新节点
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
// 同步队列未初始化,创建一个哨兵节点,CAS操作设置为同步队列队头,成功或者失败都重新进入for循环进行判断
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 同步队列已初始化,CAS添加新节点到同步队列队尾,成功则返回新节点的前驱节点,失败则重新进入for循环进行判断
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
// 步骤3:排队获取锁
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)) {
// 如果前驱节点是头结点,有可能已经释放锁,则尝试获取锁,成功则将当前节点设置为头结点,返回排队获取锁过程中线程是否被中断标识
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果前驱节点不是头结点,或者上一步尝试获取锁失败
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 如果中间线程被中断,不做处理,仅仅更新一下标志位
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 获取锁失败后判断当前线程是否可以park
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前驱节点状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
// 如果前驱节点状态为-1(SIGNAL),当前线程可以park
return true;
if (ws > 0) {
// 如果前驱节点状态为1(取消状态),则一直查找到第一个非取消状态的节点,并删除中间的取消状态的节点,当前线程不可以park
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果前驱节点状态为0(默认)或者-3(PROPAGATE),CAS设置前继节点的状态为-1(SIGNAL),当前线程不可以park
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// park当前线程,如果线程被唤醒或者被中断醒来,则返回当前线程时候被中断
LockSupport.park(this);
return Thread.interrupted();
}
// 步骤4:判断排队获取锁过程中线程是否被中断,如果被中断,就再次中断当前线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
3.2 响应线程中断
3.2.1 流程图
3.2.2 JDK源码
// 整个过程和不响应线程中断类似,区别就是响应线程中断,如果排队获取锁过程中被中断,就会抛出InterruptedException
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 如果中间线程被中断,抛出InterruptedException
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3.3 响应线程中断并判断超时
3.3.1 流程图
3.3.2 JDK源码
// 整个过程和响应线程中断类似,区别就是会判断是否超时
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
// 计算超时时间
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
// 计算剩余超时时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
// 如果获取锁超时,返回失败
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
// 细节:判断剩余时间如果大于spinForTimeoutThreshold(1000纳秒),则park当前线程,否则进入自旋判断,避免剩余时间过短,park到醒来整个过程时间过长导致超时判断不准确
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
// 如果中间线程被中断,抛出InterruptedException
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
4.独占式释放锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
// 尝试释放锁成功
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
// 尝试释放锁失败
return false;
}
// 尝试释放锁,模板方法,具体实现由子类完成
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 唤醒后继节点
private void unparkSuccessor(Node node) {
// 获取当前状态
int ws = node.waitStatus;
if (ws < 0)
// 当前状态小于0,则CAS操作将当前节点状态设置为0
compareAndSetWaitStatus(node, ws, 0);
// 获取后继节点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
// 如果后继节点不存在或者状态为已取消,则从同步队列队尾往前一直找到当前节点后面的状态<=0的节点作为后继节点
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 唤醒后继节点包装的线程
LockSupport.unpark(s.thread);
}