前言
AQS全称是AbstractQueuedSynchronize,是java JUC的核心,java现有的ReentrantLock、CoundDownLatch以及Semaphore都是基于AQS实现的,其内部又可分为独占和共享、公平和非公平
AQS工作原理
在AQS中维护着一个使用volatile修饰的全局变量state,使用它来标识同步状态,当state为0时代表着没有线程占有资源,不为0时意味着有线程占有资源;而后其他想要获取资源的线程需要先进入同步队列等待锁的释放。其内部使用Node内部类构建了一个FIFO(先进先出)的同步队列,将等待获取锁的线程放入队列中(本质上使用的是一种链式结构,标记有头尾结点head-tail)。
同时AQS还使用ConditionObject内部类构建了等待队列,当Condition调用await()方法后,会将调用线程加入等待队列当中,而当Condition调用signal,会将线程从等待队列中转移到同步队列
// 共享同步队列的头部
private transient volatile Node head;
// 共享同步队列的尾部
private transient volatile Node tail;
// 同步状态标识
private volatile int state;
上述代码使用的Node节点是每一个获取锁资源的线程的封装体,其内部包括线程本身的状态,以及其前驱节点和后继节点,其代码构成如下
static final class Node {
// 标明为共享模式(Semaphore、读锁RaadLock等就是基于该模式实现)
static final Node SHARED = new Node();
// 独占模式(ReentranLock基于该模式实现)
static final Node EXCLUSIVE = null;
// 线程状态标识-结束状态(该模式下节点需要从同步队列中取消)
static final int CANCELLED = 1;
// 线程状态标识-等待唤醒状态(该状态下,其前驱节点如果释放或被取消将会通知该节点的线程执行)
static final int SIGNAL = -1;
// 线程状态标识-Condition条件状态(该状态下,节点处于等待队列中,节点的线程等待condition条件,当调用Condition的signal()方法后,其将从等待队列转移到同步队列中)
static final int CONDITION = -2;
// 在共享模式中使用表示获得的同步状态会被传播(在共享模式中,标识为该状态的节点的线程处于可运行状态)
static final int PROPAGATE = -3;
// 等待状态,包含CANCELLED、SIGNAL、CONDITION、PROPAGATE
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;
}
// 省略代码...
}
ReentranLock中的Sync类继承自AQS,其子类又分为FairSync和Nofairync也就是公平锁和非公平锁的实现者,AQS内部提供了独占模式下以及共享模式下释放和获取锁的方法,但其实现由子类完成
// 独占模式下获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 独占模式下释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下获取锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下释放锁
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 判断是否持有独占锁
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
ReentranLock中的非公平锁以及公平锁
非公平锁(NofairSync)
加锁
// 构造方法,ReentrantLock默认构造方法使用的就是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 带参构造方法,使用布尔值fair指定使用的是公平锁构造还是非公平锁构造
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁的加锁
static final class NonfairSync extends Sync {
final void lock() {
// 执行CAS操作,修改同步状态获取锁资源,使用CAS保证原子性
if (compareAndSetState(0, 1))
// 加锁成功,将独占锁线程修改为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 修改失败,再次请求同步状态
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
流程图如下
graph LR
A[lock]-->B[执行CAS操作,尝试修改同步状态]-->C{是否修改成功}
C--成功-->D[将独占线程修改为当前线程]
C--失败-->E[再次请求同步状态]
请求同步状态的方法内部主要逻辑是:1,进入方法后执行tryAcquire(arg)方法,此处由子类NofairSync中的tryAcquire(arg)方法实现;2, 使用addWaiter将线程封装成一个Node节点,将其加入到同步队列尾部;3,进行自旋,在观察时机等待条件满足时获取同步状态,然后退出自旋,由acquireQueued()方法执行
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(arg)方法解析,由于调用的是NofairSync所以实际调用的是nonfairTryAcquire(int acqure)方法
final boolean nonfairTryAcquire(int acquires) {
// 获取当前执行线程
final Thread current = Thread.currentThread();
// 获取同步器状态标识值
int c = getState();
// 判断状态标识值是否为0,并尝试再次修改同步状态
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;
}
在tryAcquire执行完成之后,如果成功,那么可以直接能够获取锁,也就没有后续的逻辑了,如果没有加锁成功,那么需要继续执行addWaiter(Node.EXCLUSIVE)进行线程封装以及acquireQueued(Node node)获取同步状态的操作
private Node addWaiter(Node mode) {
// 将线程封装成Node节点
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入队操作
enq(node);
return node;
}
enq方法解析
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 如果尾节点为空则队列为空
if (t == null) { // Must initialize
// CAS操作创建头节点以及尾节点,此处new出来的Node节点,只是作为一个牵头节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 使用CAS向队尾加入新节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
将新节点放入同步队列之后,通过自旋,获取同步状态,其函数方法为acquiredQueued(Node node,arg)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
// 阻塞挂起标识
boolean interrupted = false;
for (;;) {
// 获取前驱节点
final Node p = node.predecessor();
// 如果p为头结点才尝试获取同步状态(此处获取的p为当前节点的前置节点)
if (p == head && tryAcquire(arg)) {
// 将node设置为头结点
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果前驱节点不是head,判断是否阻塞挂起线程,如果需要那么阻塞挂起,阻塞标识修改为true
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// 如果同步状获取失败,结束该线程请求
cancelAcquire(node);
}
}
头节点设置(setHead(Node node))方法解析
private void setHead(Node node) {
head = node;
// 当前线程已经获取到了锁资源,所以没必要存储线程信息
node.thread = null;
// 当前节点已经成为头节点,不存在前置节点
node.prev = null;
}
如果前置节点不是头结点,那么判断是否需要挂起,以及执行挂起逻辑
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取当前节点的状态
int ws = pred.waitStatus;
// 如果为等待唤醒状态,那么则返回true
if (ws == Node.SIGNAL)
return true;
// 大于0说明是结束状态,遍历前驱节点,找到没有结束状态的节点,并删除所有状态为结束的节点
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果该节点状态小于0但又不是SIGNAL状态,意味着该节点刚从条件队列中转移到同步队列,那么设置其为SIGNAL状态
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 使用LOCKSupport的park方法挂起当前线程
LockSupport.park(this);
// 获取线程的中断状态
return Thread.interrupted();
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
// 设置当前线程的监视器blocker
setBlocker(t, blocker);
// 调用native方法阻塞当前线程
UNSAFE.park(false, 0L);
// 置空blocker
setBlocker(t, null);
}
释放锁
在reentrantLock中,释放锁需要手动显示的释放锁,即调用unlock()方法
public void unlock() {
sync.release(1);
}
// AQS->release()方法
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 final boolean tryRelease(int releases) {
int c = getState() - releases;
//判断当前线程是否为持有锁的线程,如果不是,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 判断状态是否为0,如果是则说明已经释放同步状态
if (c == 0) {
free = true;
// 设置owner(锁持有的线程)为null
setExclusiveOwnerThread(null);
}
// 更新同步状态
setState(c);
return free;
}
资源释放之后,唤醒后继节点的线程,即调用unparkSuccessor(h)方法
private void unparkSuccessor(Node node) {
// 获取当前线程等待状态
int ws = node.waitStatus;
if (ws < 0)
// 使用CAS置零当前线程所在节点状态
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
//此处如果为空或者状态为结束状态,那么尾部向前遍历,找到状态不为结束的节点,将其赋值给s
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);
}
公平锁FairSync的加锁
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;
}