本文通过总结源码学习,来分析了解下AQS的工作原理
AQS是juc包锁实现的基础框架,研究juc包源码之前,AQS是必经之路
虽然说,平时项目中,我们几乎不会有自己去继承aqs实现锁的需要,但是通过源码了解aqs的机制和原理,有助于我们加深对各种锁的理解,以及出现问题时排查的思路
AbstractQueuedSynchronizer抽象队列同步器,CLH 锁
The wait queue is a variant of a “CLH” (Craig, Landin, and Hagersten) lock queue. CLH locks are normally used for spinlocks. We instead use them for blocking synchronizers, but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node. A “status” field in each node keeps track of whether a thread should block. A node is signalled when its predecessor releases.
双向FIFO等待队列,自旋锁,使用队列结点对象Node包装要获取锁的线程
AQS通过一个状态变量state,来标志当前线程持有锁的状态。
state = 0时代表没有持有锁,> 0 代表持有锁。
当队列中一个结点释放锁时,会唤醒后继阻塞的线程
内部类Node
static final class Node {
/** 共享模式的Node */
static final Node SHARED = new Node();
/** 独占模式的Node */
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;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
// 等待队列中当前结点线程
volatile Thread thread;
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
// 获取前驱结点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
如下图就是aqs同步队列中的一个节点结构,它有两个分别指向前后节点的指针,包含了当前线程thread,以及节点的状态waitStatus
waitStatus解释
其实关于waitStatus,有点不好理解的是SIGNAL这个状态
我们先来看一下源码中是如何解释的
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/**
* 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.
*/