AQS: AbstractQueuedSynchronizer,抽象队列同步器。用来实现锁或其他同步器组件的公共基础部分的抽象实现,是重量级基础框架及整个JUC体系的基石,用于解决锁分配给“谁”的问题。
常见的同步组件如ReentrantLock、ReentrantReadWriteLock、CountdownLatch等都是基于AQS实现的(通过内部类extend AQS)。
AQS内部包含一个CLH变体队列,抢不到锁的线程将会被封装成一个Node并进入此队列,而此处的锁实际上就是state变量(volatile
类型)。一旦进入队列,线程将会park
,等待后续的unpark
。
ReentrantLock有两种实现方式:公平锁、非公平锁,由于非公平锁包含公平锁相关逻辑,此处以非公平锁的源码来进行讲解。
假设队列刚开始时为空,state资源也没有占用,此处有三个线程A、B、C依次调用lock.lock()
方法
Lock lock = new ReentrantLock();
// 线程A
lock.lock();
// 线程B
lock.lock();
// 线程C
lock.lock();
线程A
针对线程A而言,直接在这一步就拿到锁返回了。
final void lock() {
// 非公平锁体现在此处,首先抢一把,抢不到才进入队列park
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
针对B、C而言,他们将以Node的方式进入队列,效果图如下:
线程B
针对线程B而言,会依次执行if里面的三个方法,最后在acquireQueued()
方法中park。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- 执行
tryAcquire()
,并返回false
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 针对线程B而言,由于state被A占用,此处c==1
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 执行
addWaiter()
方法,并继而执行enq()
方法
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;
// 此时tail==null,因为pred也为null
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
// 线程B会执行for循环两次,分别走了if...else...两个分支
for (;;) {
// 此处将t指向tail的地址,是为了确保在下面CAS之前如果其它线程设置了tail可以自旋重试
Node t = tail;
if (t == null) {
// 此处生成了一个虚拟的头结点(也就是说FIFO队列的头结点是个虚拟的空节点)
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
- 执行
acquireQueued()
方法,B将在执行第2次for循环后被LockSupport.park(this)
阻塞
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
// B线程:head节点的后一个节点会tryAcquire锁,但会失败
// C线程:它的prev节点是B,不会tryAcquire(也就是说只要进入FIFO队列了,就是公平锁了)
// 当抢到锁以后,它会替换自己前节点(header节点)的位置
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 线程B会执行2次for循环,第二次的时候会进入parkAndCheckInterrupt()
// 且通过方法LockSupport.park(this)被阻塞(线程C也是如此)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 默认的waitStatus==0,将会进入else分支,并将waitStatus改为Node.SIGNAL
// 上层方法的for语句还会再执行,下次进来时就会返回true了
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 从park方法返回有两种情况:unpark和被interrupt,如果是后者就不能再去抢锁了
LockSupport.park(this);
// Tests whether the current thread has been interrupted.
// The interrupted status of the thread is cleared by this method.
return Thread.interrupted();
}
线程C
线程C其它流程与B一致,只是addWaiter()
有点不一样。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 由于tail此时已经存在了,故直接将此node添加到tail节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
线程A释放锁
最后,当线程A执行完业务,调用lock.unlock()
方法方法后释放资源。
public final boolean release(int arg) {
// 尝试设置state-1,由于只能由持有锁的线程设置,故不存在并发问题,无需CAS
// 由于是可重入锁,故state的值可能>1
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 唤醒header的后续节点,让其从park方法处返回
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
// 如果s的后续节点为null或者CANCEL了,就要从tail开始找符合条件的节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
// 注意此处并没有return
// 一直找到离header最近的waitStatus<=0的节点
s = t;
}
if (s != null)
// 此时线程B从LockSupport.park(this)被唤醒,继续执行后续流程,理论上如果没有新的线程抢锁,就能拿到锁(只能针对队列中FIFO,header的后续节点获取锁)
LockSupport.unpark(s.thread);
}
为什么unparkSuccessor()
要从tail开始呢?这是为了避免出现并发异常,在前面enq()
中,当线程B刚好成为tail,而还未执行if代码块时,此时的状态如下。如果不从tail往前,那么由于head的next为null,将会漏掉该Node。而可以发现的是enq()
方法是先执行了node.prev = t
再进行CAS的,故从后往前就不会有问题。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// 此处只要CAS成功,就肯定建立了上图中的连接,但可能next未建立
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
异常情况
以上是正常流程,而如果上述流程失败,failed=false因为某种异常未执行,将会进入finally的if分支。
比如tryAcquire()
方法抛throw new Error(“Maximum lock count exceeded”)异常,那么就会进入cancelAcquire()
方法。
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
// 如果前置节点也是取消的,向前找,直到是非取消的节点为止
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
// 既然已经failed了,此处就是CANCELLED
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
// 此时被cancel的节点恰好是tail E,节点D将成为tail
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
// 中间节点的CACEL,此部分代码在AQS,可能是需要兼容其它的实现类,故有点复杂
int ws;
// 非header的下一个节点,直接指向node的下一个节点(中间可能会跨越多个CANCEL的节点)
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 如果node前面已经是header节点了,既然node已经失败了,那就唤醒下一个节点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
可以分三种情况调用cancelAcquire()
分析:
- 节点E:直接把D的next置为null
- 节点D取消:C.next -> E
- 节点C、D取消:B.next -> E
参考:尚硅谷JUC:https://www.bilibili.com/video/BV1ar4y1x727?vd_source=2bea06318ac5c165ddf39315231c3508