AQS,全称:AbstractQueuedSynchronizer,是JDK提供的一个同步框架,内部维护着FIFO双向队列,即CLH同步队列。
AQS依赖它来完成同步状态的管理(voliate修饰的state,用于标志是否持有锁)。如果获取同步状态state失败时,会将当前线程及等待信息等构建成一个Node,将Node放到FIFO队列里,同时阻塞当前线程,当线程将同步状态state释放时,会把FIFO队列中的首节的唤醒,使其获取同步状态state。
很多JUC包下的锁都是基于AQS实现的
节点代码:
static final class Node {
/** 共享节点 */
static final Node SHARED = new Node();
/** 独占节点 */
static final Node EXCLUSIVE = null;
/** 因为超时或者中断,节点会被设置成取消状态,被取消的节点不会参与到竞争中,会一直是取消
状态不会改变 */
static final int CANCELLED = 1;
/** 后继节点处于等待状态,如果当前节点释放了同步状态或者被取消,会通知后继节点,使其得以
运行 */
static final int SIGNAL = -1;
/** 节点在等待条件队列中,节点线程等待在condition上,当其他线程对condition调用了signal
后,该节点将会从等待队列中进入同步队列中,获取同步状态 */
static final int CONDITION = -2;
/**
* 下一次共享式同步状态获取会无条件的传播下去
*/
static final int PROPAGATE = -3;
/** 等待状态 */
volatile int waitStatus;
/** 前驱节点 */
volatile Node prev;
/** 后继节点 */
volatile Node next;
/** 获取同步状态的线程 */
volatile Thread thread;
/**
* 下一个条件队列等待节点
*/
Node nextWaiter;
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;
}
}
独占式同步状态过程
/**
* 独占式获取同步状态
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
tryAcquire
尝试去获取锁,获取成功返回true,否则返回false。该方法由继承AQS的子类自己实现。采用了模板方法设计模式。
如:ReentrantLock的Sync内部类,Sync的子类:NonfairSync(非公平锁)和FairSync(公平锁)
FairSync:内部重写了tryAcquire方法,一下是未重写的
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
FairSync也是ReentrantLock的内部类,继承Sync
里面重写了两大方法,一个是重写了lock方法,另外一个是tryAcquire方法
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 公平版的tryAcquire,尝试获取锁
*/
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取锁的状态,state是AQS的属性,表示锁的同步状态
int c = getState();
//判断锁的状态,如果为0那么锁就是释放状态
if (c == 0) {
//若锁为释放状态,那么先判断是否处于队列头部
//若处于队列头部那么CAS尝试获取锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//若锁为释放状态,且通过cas争抢到了锁,那么将该线程赋值给AbstractOwnableSynchronizer的exclusiveOwnerThread属性
setExclusiveOwnerThread(current);
//返回争抢到了锁
return true;
}
}
//如果锁处于未释放状态那么判断可重入性
//若当前线程就是AbstractOwnableSynchronizer的exclusiveOwnerThread,那么标记重入
else if (current == getExclusiveOwnerThread()) {
//c就是当前state,acquires是传入的参数
int nextc = c + acquires;
//一般不可能加成0的,程序健壮性
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//将state状态设置为原本的状态值加上获取次数
setState(nextc);
//返回争抢到了锁(重入锁)
return true;
}
//若没有争抢到锁,同时也不是可重入锁的主人,那么就返回false表示没抢到
return false;
}
}
AQS内部维护的acquire方法,在本片开头就有,目的是尝试获取锁,在尝试的过程中,第一步就是调用try Acquire方法看是否能够获取锁。
public final void acquire(int arg) {
//第一步判断是否抢到了锁,若没抢到,开始第二步入队列
//addWaiter方法就是添加队列的方法,其中node的Exclusive属性(独占节点)默认是null
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter(第一次添加队列)
//mode值就是Node节点的final静态变量:EXCLUSIVE,默认是null,所以此处mode为null
private Node addWaiter(Node mode) {
// 新建Node节点,该node的Thread值就是null,而不是没抢到锁的节点
Node node = new Node(Thread.currentThread(), mode);
// 设置一个pred,将Node的tail赋值给pred,尝试快速添加尾结点
Node pred = tail;
// 第一次添加,AQS的tail尾部一定是null,所以此处头结点也是null
// 第一次添加,一定不成立,那么跳过if来到enq方法
if (pred != null) {
node.prev = pred;
// CAS方式设置尾结点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果上面添加失败,这里循环尝试添加,直到添加成功为止(通过自旋CAS进行添加操作)
// 注意此处的node节点中的线程就是目前线程的节点
enq(node);
return node;
}
enq(CAS自旋添加)
private Node enq(final Node node) {
// 一直for循环,直到插入Node成功为止
for (;;) {
//拿到AQS指向的尾节点
Node t = tail;
if (t == null) {
// CAS设置首节点,如果设置不成功,那么又会来到下一次循环进行设置,设置的过程CAS原子性
// 注意,这里不是目前线程所在节点,而是new了一个新的空节点
if (compareAndSetHead(new Node()))
//设置成功,AQS指向的头部变成了新的空节点,开始下一次循环
tail = head;
} else {
//最后一次循环,AQS的尾部已经有尾部节点了,那么来到else这一边
//将尾部节点赋值给
node.prev = t;
// CAS设置尾结点
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
//此后的尾节点有两种,一种的包含着线程的尾节点,还有一种是队中只有一个的空节点
}
}
}
acquireQueued
方法返回到acquire,此时开始acquireQueued操作
final boolean acquireQueued(final Node node, int arg) {
// 操作是否成功标志
boolean failed = true;
try {
// 线程中断标志
boolean interrupted = false;
// 不断的自旋循环
for (;;) {
// 当前节点的prev节点
final Node p = node.predecessor();
// 判断prev是否是头结点 && 是否获取到同步状态
// 获取当前线程所在队列节点的前一个节点,看前一个节点是否是头结点(也就是空节点)
//如果是空节点,那么该线程所在位置就是队列的第一,那么此时可以尝试获取锁tryAcquire
if (p == head && tryAcquire(arg)) {
// 当前线程节点正好是队一,且抢到了锁
// 将当前节点设置成头结点
setHead(node);//此时空节点还在
// 将prev节点移除队列中
// 空节点无了,换成了当前线程的节点
p.next = null; // help GC垃圾回收
//获取成功
failed = false;
//此时线程没有中断,返回false,acquire方法返回,lock方法返回,当前线程获得锁
//此时的当前线程所在的节点扮演了原来空节点的角色
return interrupted;
}
// 不在队头,或在队头没抢到锁,那么开始自旋,如果自旋到一定次数(一次),那么阻塞
// 自旋过程中,判断当前线程是否需要阻塞 && 阻塞当前线程并且检验线程中断状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//自旋一次的目的是尽量争抢执行权,防止被park
interrupted = true;
}
} finally {
if (failed)
// 取消获取同步状态
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 拿到当前节点的prev节点的等待状态,如果没有改动,那么默认等待状态为0
int ws = pred.waitStatus;
//SIGNAL是标记量,标记当前节点是否释放了同步状态或者被取消
//如果当前线程第一次自旋,那么ws一般都是0
if (ws == Node.SIGNAL)
/*
* 如果prev的status是signal,表示当prev释放了同步状态或者取消了,会通知当前节
* 点,所以当前节点可以安心的阻塞了(相当睡觉会有人叫醒他)
*/
return true;
if (ws > 0) {
/*
* status > 0,表示为取消状态,需要将取消状态的节点从队列中移除
* 直到找到一个状态不是取消的节点为止
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 除了以上情况,通过CAS将prev的status设置成signal
*/
// 自旋一次结束,将前一个节点的状态设置为释放同步或者取消
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
LockSupport.park(this);
// 返回当前线程的中断状态
return Thread.interrupted();
}