一、重温AQS的基本方法
上文中提到了AQS背后的设计原理,本文继续深入细节,分析AQS的核心源码设计
二、独占式获取
1 public final void acquire(int arg) {
2 if (!tryAcquire(arg) &&
3 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
4 selfInterrupt();
5 }
-
第2行中tryAcquire方法是留给子类重写的方法,自定义获取逻辑,tryAcquire返回成功时,意味着获取到同步状态,方法跳出结束;tryAcquire返回失败时,则代表获取同步状态失败,此时就需要加入队列中,也就是第3行。
-
第3行先调用addWaiter()方法,然后将其结果作为参数再调用acquireQueued()方法。
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) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
addWaiter()方法中Node.EXCLUSIVE代表独占性类型节点,同时将Thread.currentThread()当前线程作为入参构建队列新节点node。
我们知道AQS使用的队列是FIFO先进先出队列,从尾部进行插入。先判断尾节点是否为空,不为空则将尾节点作为新节点node的prev前置节点;同时使用cas让tail节点指向新的node节点, 更新成功则返回node;如果尾节点不为空或者cas更新tail节点失败,则执行enq(node)方法。
所以enq()方法需要处理:1. 队列尾节点为空时的入队操作;2. CAS尾节点插入失败后负责死循环自旋重试。
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;
}
}
}
}
第一种情况则说明是队列的唯一的节点,head和tail都指向头结点,然后继续循环走到第二种情况。
第二种情况则是tail节点不为空时,cas死循环插到尾部,直到成功才结束返回。
Node状态 | 说明 |
---|---|
0 | 初始状态 |
-1 | SIGNAL,后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,那么就会通知后继节点,让后继节点的线程能够运行 |
-2 | CONDITION,节点在等待队列中,节点线程等待在Condition上 |
-3 | PROPAGATE,表示下一次的共享式同步状态获取将会无条件的被传播下去 |
1 | CANCELLED,由于超时或中断,该节点被取消 |
node节点成功插入返回后,进入acquireQueued方法,if (p == head && tryAcquire(arg)))判断此节点的前置节点如果是头结点并且可以成功获取同步状态时,就可以获得独占式锁。值得注意的是,此时的tryAcquire方法是需要子类重写的方法。
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);
}
}
获得独占式锁后,通过setHead()方法将当前节点设置为头结点,再将之前的头结点与队列断开,成功出队。
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
如果**前置节点不是头结点或者获取锁失败的时候**会调用shouldParkAfterFailedAcquire()方法和parkAndCheckInterrupt()方法。 先将节点状态设置成SIGNAL,然后调用LookSupport.park方法将当前线程阻塞。
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;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
三、独占式释放
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
通过子类的tryRelease方法实现释放操作,成功释放后通过unparkSuccessor唤醒头节点next节点对应的线程。
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
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);
}
四、独占式可中断获取
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())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
与acquire独占式获取的区别在于当parkAndCheckInterrupt返回true时,即线程阻塞时该线程被中断,代码抛出被中断异常。
五、超时独占式可中断获取
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)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
是在独占式可中断获取的基础上,增加了对超时时间的控制。每一次循环时会校验当前时间是否已超过最大超时时间。
六、共享式获取
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
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);
}
}
代码和独占式获取几乎一样,区别在于tryAcquireShared方法大于0时为成功获取同步状态的条件
七、共享式释放
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
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;
}
}