定义
全称是AbstractQueuedSynchronizer,AQS是JUC提供的一个用于构建锁和同步容器的基础类。JUC包内许多类都是基于AQS构建的,例如ReentrantLock、 Semaphore、CountDownIatch、ReentrantReadWriteLock、 FutureTask 等。通过内置的 CLH(FIFO)队列的变种来完成资源获取线程的排队⼯作,将每条将要去抢占资源的线程封装成⼀个 Node节点来实现锁的分配,有⼀个int类变量表示持有锁的状态,通过CAS完成对status值的修改。这种结构的特点是每个数据结构都有两个指针,分别指向直接的前驱节点和直接的后继节点。所以双向链表可以从任意-一个节点开始很方便地访问前驱节点和后继节点。每个节点其实是由线程封装的,当线程争抢锁失败后会封装成节点加入AQS队列中;当获取锁的线程释放锁以后,会从队列中唤醒一一个阻塞的节点
本文以ReentrantLock作为具体实现来讲解AQS 的加锁流程,具体源码之前先看下AQS 和ReentrantLock的关系
在看具体实现源码之前咱们先看看关键的Node 的对象结构和AQS 队列的基本结构
Node 结构
public class Node{
volatile Node prev; //上一个节点
volatile Node next; //下一个节点
volatile Thread thread; //当前node包含的线程对象
}
AQS 同步队列结构
stats 表示重入次数,head 表示队头,tail 表示队尾。初始化状态时 status 为0 ,head 和tail 都为null,thread 为null。
加锁流程
以下以ReentrantLock 的公平锁为例,非公平锁与之类似,有关公平锁和非公平锁参考我之前的文章。
第一种情况
第一个线程t0获取锁的时候没有其他线程, 代价基本为0(cas改变锁的状态,继而记录当前持有锁的线程);队列的状态如下图
大致流程:
public final void acquire(int arg) {
// 如果能够获取到锁,则不会进行入队操作
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
}
}
第二种情况
t0 没有释放当前锁,t1 此时来加锁,此时队列的初始状态为第一种情况的样子,此时t1会加加锁失败;返回false,然后调用addWaiter方法区初始化队列,然后自己入队;
核心源码
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;
}
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;
}
}
}
}
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);
}
}
第三种情况
t2来加锁,t0依旧没有释放锁,t1此时在队列中, 与第二种情况类似,只是由于前一个节点不是head,不需要进行自旋,直接入队即可,最终队列的情况为