AQS源码剖析
AQS源码剖析
AQS就是AbstractQueuedSynchronizer抽象类,AQS其实就是JUC包下的一个基类,JUC下的很多内容都是基于AQS实现了部分功能,比如ReentrantLock,ThreadPoolExecutor,CountDownLatch,Semaphore,CyclicBarrier等等都是基于AQS实现。
首先AQS中提供了一个由volatile修饰,并且采用CAS方式修改的int类型的state变量。
其次AQS中维护了一个双向链表,有head,有tail,并且每个节点都是Node对象。
static final class Node {
volatile Node prev;
volatile Node next;
volatile Thread thread;
}
内部常用方法:
// 尝试获取独占锁, 需要⼦类实现,获取成功返回true,获取失败返回false
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 尝试释放独占锁, 需要⼦类实现,释放成功返回true,释放失败返回false
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 尝试获取共享锁, 需要⼦类实现,获取成功返回 1,获取失败返回 -1
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 尝试释放共享锁, 需要⼦类实现,释放成功返回 1,释放失败返回 -1
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 添加结点到链表队尾
private Node addWaiter(Node mode) {
// ......
}
// 如果addWaiter尝试添加队尾失败,则再次调⽤enq此⽅法⾃旋将结点加⼊队尾
private Node enq(final Node node) {
// ...
}
// 释放锁时,该⽅法需要负责唤醒后继节点
private void unparkSuccessor(Node node) {
// ...
}
//检测结点状态,如果可以休眠的话则设置waitStatus=SIGNAL并调⽤LockSupport.park休息;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// ......
}
获取锁源码:
public final void acquire(int arg) {
// 尝试获取锁,比如第一个线程获取到锁了,直接返回,第二个线程没有获取到锁,将自己封装成node节点并添加到队列中
if (!tryAcquire(arg) &&
// 注意:节点类型是EXCLUSIVE
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
// 通过CAS方式向队列中追加节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 以上CAS追加如果没有成功,出现并发冲突,也就是说向链表追加节点失败,则进入enq(node)自旋重试。
enq(node);
return node;
}
// 入参:node就是当前线程的node
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取前驱节点
final Node p = node.predecessor();
// 如果前驱节点是头结点,则再次执行tryAcquire尝试获取锁资源,
// 说白了紧接着就是当前node节点要执行任务了,尝试获取一下锁,看看能不能获取成功
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);
}
}
释放锁:
释放锁说白了就是当前节点释放锁资源,让后继结点持有锁,执行任务。
public final boolean release(int arg) {
// 释放锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 如果释放锁成功,则唤醒后面排队的线程
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.
*/
// 如果后续节点为null,或者后续节点状态为取消状态,从后往前找到一个有效节点进行唤醒
// 说白了,就是如果后继结点不是一个有效节点,则会从后往前找
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);// 唤醒和挂起操作底层使用的是UNSAFE类的park()和unpark()方法。
}