AQS源码详解
1. 概述
AQS(AbstractQueuedSynchronizer )字面意思是抽象队列同步器,是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石。通过内置的FIFO队列
来完成资源获取线程的排队工作,并通过一个int类变量
表示持有锁的状态。
AQS解决的问题主要是,在加锁的时候会产生阻塞,此时就需要排队。而实现排队就需要某种类型的队列去进行管理。并且排队的线程也需要被唤醒以保证锁的分配。这些都由AQS来进行解决。总结一下可以分为两点:
(1)对排队的线程进行管理。(2)唤醒排队的线程以确保锁的分配
官网的解释:
Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues.
提供一个框架来实现阻塞锁和依赖先进先出(FIFO)等待队列的相关同步器(信号量、事件等)。
2. AQS的结构
AQS主要分为两个主要部分:
-
用一个volatile的
int类型的成员变量state
来表示同步状态/** * The synchronization state. */ private volatile int state;
-
一条基于CLH队列的变体实现的双向队列,用于排列被包装为Node节点的线程
* <p>To enqueue into a CLH lock, you atomically splice it in as new * tail. To dequeue, you just set the head field. * <pre> * +------+ prev +-----+ +-----+ * head | | <---- | | <---- | | tail * +------+ +-----+ +-----+ static final class Node { /** Marker to indicate a node is waiting in shared mode */ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE = null; /** waitStatus value to indicate thread has cancelled */ static final int CANCELLED = 1; /** waitStatus value to indicate successor's thread needs unparking */ static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition */ static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ static final int PROPAGATE = -3; /** * Status field, taking on only the values: * SIGNAL: The successor of this node is (or will soon be) * blocked (via park), so the current node must * unpark its successor when it releases or * cancels. To avoid races, acquire methods must * first indicate they need a signal, * then retry the atomic acquire, and then, * on failure, block. * CANCELLED: This node is cancelled due to timeout or interrupt. * Nodes never leave this state. In particular, * a thread with cancelled node never again blocks. * CONDITION: This node is currently on a condition queue. * It will not be used as a sync queue node * until transferred, at which time the status * will be set to 0. (Use of this value here has * nothing to do with the other uses of the * field, but simplifies mechanics.) * PROPAGATE: A releaseShared should be propagated to other * nodes. This is set (for head node only) in * doReleaseShared to ensure propagation * continues, even if other operations have * since intervened. * 0: None of the above * * The values are arranged numerically to simplify use. * Non-negative values mean that a node doesn't need to * signal. So, most code doesn't need to check for particular * values, just for sign. * * The field is initialized to 0 for normal sync nodes, and * CONDITION for condition nodes. It is modified using CAS * (or when possible, unconditional volatile writes). */ volatile int waitStatus; /** * Link to predecessor node that current node/thread relies on * for checking waitStatus. Assigned during enqueuing, and nulled * out (for sake of GC) only upon dequeuing. Also, upon * cancellation of a predecessor, we short-circuit while * finding a non-cancelled one, which will always exist * because the head node is never cancelled: A node becomes * head only as a result of successful acquire. A * cancelled thread never succeeds in acquiring, and a thread only * cancels itself, not any other node. */ volatile Node prev; /** * Link to the successor node that the current node/thread * unparks upon release. Assigned during enqueuing, adjusted * when bypassing cancelled predecessors, and nulled out (for * sake of GC) when dequeued. The enq operation does not * assign next field of a predecessor until after attachment, * so seeing a null next field does not necessarily mean that * node is at end of queue. However, if a next field appears * to be null, we can scan prev's from the tail to * double-check. The next field of cancelled nodes is set to * point to the node itself instead of null, to make life * easier for isOnSyncQueue. */ volatile Node next; /** * The thread that enqueued this node. Initialized on * construction and nulled out after use. */ volatile Thread thread; }
关于state变量
state表示同步状态,state = 0表示锁空闲可以使用,而state >= 1表示锁被占用。整个state被volatile修饰,保证其可见性。
关于Node节点
从上面的定义可以看出,Node节点里定义了状态位waitStatus,同样用volatile修饰,用于表示当前Node节点的状态。同时有一个前指针和后指针,表明这是一个双端队列。同时在AQS中还定义了一个头指针和尾指针,永远指向头结点和为尾节点。
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
因此我们可以画出AQS的简单结构图以供理解:
3. 源码实例理解(以ReentrantLock为例)
ReentrantLock默认为非公平锁,这里就涉及到公平锁和非公平锁的概念。公平锁和非公平锁的定义如下:
公平锁:公平锁讲究先来先到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入等待队列中。
非公平锁:不管是否有等待队列,如果可以获取锁,则立刻占有锁对象。也就是说队列的第一个排队线程在unpark(), 之后还是需要竞争锁(存在线程竞争的情况下)
公平锁代码:
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
非公平锁代码:
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}
对比公平锁和非公平锁的tryAcqure()方法的实现代码, 其实差别就在于非公平锁获取锁时比公平锁中少了一个判断!hasQueuedPredecessors()。hasQueuedPredecessors()主要是判断是否需要排队。这也是两者之间最大的区别。
接下来我们可以从公平锁和非公平锁的lock()方法看起,公平锁的lock()方法就是直接调用acquire()方法,而非公平锁的lock()方法会先判断锁是否空闲,空闲则占有,反之也会调用acquire()方法。所以我们可以看到公平锁和非公平锁最终都会调用acquire()方法。
因此我们可以看一下acquire()方法,具体如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这边我们可以看到有三个方法,分别是tryAcquire()、addWaiter()和acquireQueued()方法。下面我们对每个方法进行具体的分析:
tryAcquire()方法(以非公平锁为例)
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread(); //获取当前线程:此时是顾客B
int c = getState(); //获取state状态,此时值为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;
}
通过nonfairTryAcquire()方法我们可以知道,首先会对整个AQS的state变量进行判断,如果为0,就意味着此时锁空闲,那就使用CAS尝试获取。获取成功则将当前线程设置为此时掌握锁的线程并返回true。若不为0,即此时锁已被占有,则会判断此时占有锁的线程是不是当前线程,是的话则计算state的值并进行后续判断,这一块主要是针对可重入锁的。若都不满足则返回false。
我们再回到之前的这个判断语句,如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当tryAcquire()方法返回true的时候,由于取反则返回false,根据&&的相关特性,如果为false则直接跳过后面的判断条件,会直接进行中断即获取到锁。若返回值为false,取反则为true,此时就要看后面的条件,即另外两个方法。
addWaiter()方法
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
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节点,然后对尾指针进行判断,若为空说明队列里此时没有等待线程结点,此时会调用enq()方法,最后返回结点。
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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;
}
}
}
}
通过enq()方法的源码我们可以看到这是一种自旋操作,具体操作首先对尾指针进行判断,为空则建立一个新的Node()节点即一个傀儡节点,同时头尾指针都指向它。若不为空,则将节点挂到队列尾。再回到前面,我们可以看到当尾节点不为null的时候,就直接将节点挂到队列尾即可。这两部分代码都是一样的。
acquireQueued()方法
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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);
}
}
再看最后一个方法,我们前面通过addWaiter()方法返回了一个Node结点,而acquireQueued()方法也是通过一个自旋操作对返回的节点进行判断。
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
predecessor()方法就是得到当前节点的前驱节点,而判断条件
if (p == head && tryAcquire(arg))
就是判断这个节点不是傀儡节点,并且此时已经成功获取到锁,此时会进行两个操作,一个就是将持有锁的线程设置为当前线程;另一个就是将队列中的此线程变成傀儡节点,并将傀儡节点删除以便后续的GC回收。而若果不满足这个条件则进入下一个判断
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
shouldParkAfterFailedAcquire()方法如下
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev.
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
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;
}
可以看到这个方法会对每个Node结点中的waitStatus变量进行判断,不同的值代表不同的情况。针对不同的值会有不同的返回值。当ws大于1时表示当前线程获取锁的请求被取消了,我们可以看到节点会被删除,然后返回false,最终取消。当ws为Node.SIGNAL的时候,表示此时需要被挂起,返回true。我们再回到之前的判断条件,进入parkAndCheckInterrupt()方法
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
这个方法调用LockSupport.park()方法将此线程进行阻塞,只有当锁释放的时候,才能解开。我们再来看unlock方法
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//此时c就为0
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null); //将当前拥有锁的线程设置为null
}
setState(c); //同时设置state值为0
return free;
}
当调用unlock()方法时,能进入unparkSuccessor(h)
方法:
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; //此时为-1
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); //进入,通过CAS操作将状态设置为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) //upark唤醒线程
LockSupport.unpark(s.thread);
}
我们可以看到调用了LockSupport.unpark()方法,此时线程的阻塞状态才会被解除。再回到前面的代码:
final boolean acquireQueued(final Node node, int arg) { //顾客B
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);
}
}
此时由于是一个自旋的操作,因此会再次尝试获取锁,若成功则返回false,不成功则再次进入阻塞状态等待唤醒。在回到最初的判断条件
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当都为false的时候,由于都取反,因此都为true,此时产生中断,成功获取到锁。所有的过程到此完成。