【JUC】AQS的源码理解
引言
AQS提供了一个抽象的线程并发同步器,其中维护了一个表示共享资源的volatile int state和一个FIFO线程等待队列。
AQS定义了两种资源共享方式:Exclusive(独占,如ReentrantLock)和Share(共享,如Semaphore/CountDownLatch)。自定义同步器在实现时只需要实现共享资源state的具体获取与释放方式,至于等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。
节点状态Node.waitStatus
Node结点是对每一个等待获取资源的线程的封装,其中包含了需要线程对象的引用及等待状态。变量waitStatus表示当前Node结点的等待状态,共有5种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE、0。
CANCELLED(1):表示当前结点已取消调度。
0:新结点的初始状态。
SIGNAL(-1):表示当前节点需要unpark后继节点。
CONDITION(-2):表示结点等待在Condition上。
PROPAGATE(-3):共享模式下,防止并发释放共享资源时因头节点状态短暂为0而唤醒链中断的问题。
acquire(int arg)
此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源就直接返回,否则将当前线程封装成节点放进等待队列并park当前线程。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
函数流程如下:
1.tryAcquire()尝试直接去获取资源,如果成功则返回true。tryAcquire()的具体实现由子类负责,子类可以自主决定可重入性与公平性。
2.addWaiter()将当前线程封装成节点放进等待队列的尾部,并标记为独占模式。
3.acquireQueued()使线程找到可以被前一个节点唤醒的位置,然后park在等待队列中。在acquireQueued()的实现中是不响应中断与超时的,在等待过程中被中断后会临时保存中断状态继续等待,只有在获取资源后才会进行自我中断selfInterrupt()将中断补上。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquire
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// java.util.concurrent.locks.ReentrantLock.Sync.nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
// java.util.concurrent.locks.ReentrantLock.FairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
tryAcquire尝试一次获取独占资源。如果获取成功,则直接返回true,否则直接返回false。在Reentrantlock的实现中,通过if (current == getExclusiveOwnerThread())实现了可重入性,通过if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))实现了公平性。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.addWaiter
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.enq
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
addWaiter为当前线程创建节点,接着尝试直接将节点插入队尾,如果成功则返回,否则进入enq(node)。在enq(node)中,如果等待队列为空则创建头节点,接着一直自旋将当前节点插入队尾。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued
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);
}
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.setHead
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
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;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
acquireQueued会让当前线程进入死循环,在不响应中断与超时的情况下只有获得了锁才能退出循环。没有获取到锁的线程会park等待被唤醒尝试获取共享资源,最后返回等待过程中临时保存的中断状态。
shouldParkAfterFailedAcquire会检查前驱结点的状态,如果if (ws == Node.SIGNAL)说明前驱结点会负责unpark当前节点,那么当前节点可以直接park,因此会返回true;如果if (ws > 0)说明前驱放弃等待了,那就一直往前找到最近的非放弃等待的节点,排在它的后边;否则就把前驱的状态设置成SIGNAL,告诉它需要负责通知自己。
parkAndCheckInterrupt会park当前线程,线程醒来后会回到死循环中。如果线程被中断唤醒了,在AQS的默认逻辑中,此时会返回死循环中将中断状态临时保存再次进入循环。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.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.
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
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.
int ws;
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 {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
cancelAcquire()只有当节点放弃等待锁时才会被调用,在AQS的默认逻辑中不会被调用,在Reentrantlock的超时模式或可中断模式下可以被调用。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.selfInterrupt
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
在线程获取到共享资源后,根据acquireQueued返回的结果决定是否中断自己。
release(int arg)
此方法是独占模式下线程释放共享资源的顶层入口。它会释放指定量的重入次数,如果彻底释放了(即state=0),它会唤醒等待队列里的其他线程来获取资源。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.release
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.tryRelease
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
release调用tryRelease来释放资源,根据返回的布尔值来判断该线程是否已经完成释放掉资源了,如果返回true说明独占资源已被彻底释放,如果等待队列中有节点需要唤醒,调用unparkSuccessor唤醒等待线程。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.unparkSuccessor
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;
if (s == null || s.waitStatus > 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);
}
unparkSuccessor唤醒函数参数节点的后继节点。首先会将当前节点的状态设置为0,如果后继第一个节点没有放弃获取锁,那么直接唤醒它;否则就会从后往前寻找第一个未放弃的节点然后唤醒。
寻找最前边的未放弃节点是通过从后往前找的方式,这是因为由于并发问题,addWaiter()入队操作和cancelAcquire()取消排队操作都可能会造成next链的不一致,而prev链是强一致的,所以从后往前找是安全的。
acquireShared(int arg)
此方法是共享模式下线程获取共享资源的顶层入口。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireShared
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
acquireShared会调用tryAcquireShared尝试释放共享资源,返回负数表示获取共享资源失败需要调用doAcquireShared进入等待队列;0表示获取成功但没有剩余资源;正数表示获取成功且还有剩余资源。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
doAcquireShared将当前线程加入等待队列并找到合适位置后park,直到其他线程释放资源并唤醒自己成功拿到相应量的共享资源后才返回。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.setHeadAndPropagate
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
在线程获取了需要的共享资源后,调用setHeadAndPropagate方法将自身设置为头结点,接着满足条件的话会调用doReleaseShared唤醒后续节点。
releaseShared(int arg)
此方法是共享模式下线程释放共享资源的顶层入口。它会释放指定量的资源,如果成功释放且允许唤醒等待线程,它会唤醒等待队列里的其他线程来获取资源。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.tryReleaseShared
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
在独占模式下必须要当前线程完全释放锁才能唤醒等待队列的线程,但在共享模式下只要当前共享资源的剩余量满足需要就允许线程并行,因此拥有资源的线程在释放掉部分资源时就可以尝试唤醒等待结点。
// java.util.concurrent.locks.AbstractQueuedSynchronizer.doReleaseShared
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
doReleaseShared将头结点状态修改为0,接着unparkSuccessor(h)唤醒后继节点。if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))可以解决共享锁在并发释放时的唤醒链断裂问题,避免线程饥饿。
Reentrantlock
Reentrantlock在AQS的基础上实现了非阻塞尝试、可重入、公平、中断、超时放弃。
// java.util.concurrent.locks.ReentrantLock.tryLock
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
// java.util.concurrent.locks.ReentrantLock.Sync.nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
在tryRelease中,Reentrantlock先尝试将state从0CAS修改为1,如果成功则获取锁成功;如果state不为0但当前线程本身持有锁则会将重入次数加1返回成功;否则说明当前其他线程在持有锁返回失败。
// java.util.concurrent.locks.ReentrantLock.Sync.nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
// java.util.concurrent.locks.ReentrantLock.FairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
无论是非公平模式还是公平模式,通过检查当前线程是否持有锁if (current == getExclusiveOwnerThread()),然后int nextc = c + acquires实现可重入。在公平模式中,当state为0时通过if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))检查是否在等待队列中还有线程在等待实现公平性。
// java.util.concurrent.locks.ReentrantLock.lockInterruptibly
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly
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())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在doAcquireInterruptibly中,可中断模式下被中断后会抛出异常。
// java.util.concurrent.locks.ReentrantLock.tryLock
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireNanos
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)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在doAcquireNanos中,通过LockSupport.parkNanos(this, nanosTimeout)与if (nanosTimeout <= 0L) return false;实现超时放弃。
读写锁
读写锁的state中前16位保存的是读锁的总重入次数,后16位保存写锁的重入次数,每个持有读锁的线程的重入次数通过ThreadLocal的子类保存在每个线程实例的ThreadLocalMap成员变量中。
readerShouldBlock在公平模式与非公平模式有不同的实现,以此实现在新线程申请读锁时如果已经有线程等待写锁情况下的公平性。
除了可重入、公平,读写锁还支持非阻塞尝试、中断与超时放弃,这些特性的实现与Reentrantlock类似。
浙公网安备 33010602011771号