AQS
AbstractQueuedSynchronizer
队列同步器,是 JDK1.5
提供的一个基础框架,用于构建依赖于先进先出(FIFO
)等待队列的阻塞锁以及相关同步器(包括 ReentrantLock、CountDownLatch、Semaphore
等),它使用一个 int
类型的成员变量 state
表示同步状态,同步器提供了一系列模板方法来访问修改同步状态:
getState()
:获取当前同步状态
setState(int newState)
:设置当前同步状态
compareAndSetState(int expect, int update)
:CAS
设置同步状态(保证原子性)
/**
* 等待队列的头节点,延迟初始化,除了初始化,只能通过setHead修改
* 头节点存在,它的waitStatus不能是CANCEllED
*/
private transient volatile Node head;
// 等待队列的尾节点,延迟初始化,只能通过enq方法添加新的等待节点
private transient volatile Node tail;
// 同步状态
private volatile int state;
头结点和尾结点都使用 volatile
修饰,保证了可见性。
// 节点以共享模式等待的标识
static final Node SHARED = new Node();
// 节点以独占模式等待的标识
static final Node EXCLUSIVE = null;
// 表示线程被取消
static final int CANCELLED = 1;
// 表示后续节点线程需要被唤醒
static final int SIGNAL = -1;
// 表示线程正在condition上等待
static final int CONDITION = -2;
// 表示下一次共享式同步状态获取将会无条件传播下去
static final int PROPAGATE = -3;
// 等待状态 仅有上述取值和初始值0
volatile int waitStatus;
// 当前节点前驱节点 入队时分配 出队时清空(GC原因)
volatile Node prev;
// 当前节点后继节点
volatile Node next;
// 入队节点线程 构造中初始化 使用完毕清空
volatile Thread thread;
// 等待队列的后继节点
Node nextWaiter;
同步队列结构图
源码分析
独占锁
独占锁获取
/**
* 以独占模式获取,忽略中断,通过调用至少一次tryAcquire来实现,成功时返回
* 否则线程会排队等待,可能重复阻塞和解除阻塞,通过调用tryAcquire直到成功
* 改方法可以用于实现Lock的lock()方法
*/
public final void acquire(int arg) {
/**
* tryAcquire:尝试以独占方式获取,该方法应该查询当前状态是否允许独占模式获取,如果可以则获取
* addWaiter:使用给定模式为当前线程创建入队节点
* acquireQueued:为队列中的线程以独占不可中断模式获取同步状态
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 中断当前线程
selfInterrupt();
}
创建入队节点
// AbstractQueuedSynchronizer
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; // 设置当前节点的前驱节点为tail尾结点
// CAS设置当前节点为尾结点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 节点插入队列
enq(node);
return node;
}
将当前节点插入到队列
// AbstractQueuedSynchronizer
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
// 初始化CAS创建空节点
if (compareAndSetHead(new Node()))
tail = head; // CAS设置成功 将头结点赋值给尾结点,初始状态,头节点和尾节点都指向空节点
} else {
// 将当前节点的前驱节点指向尾节点
node.prev = t;
if (compareAndSetTail(t, node)) { // CAS设置尾节点
t.next = node; // 设置尾节点的后继节点为当前节点
return t;
}
}
}
}
在 acquireQueued
方法中通过自旋获取同步状态,有一个条件是只有当前节点的前驱节点是头节点才能够尝试获取同步状态,为什么会有这个判断?
原因:因为头节点是成功获取到同步状态的节点,而头节点的线程释放了同步状态后,将会唤醒其后继节点,后继节点的线程被唤醒后需要检查自己的前驱节点是否为头节点。
// AbstractQueuedSynchronizer
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; // 帮助GC
failed = false; // 设置再次获取锁成功
return interrupted; // 返回中断标识
}
// 判断获取锁失败后是否需要阻塞 如果需要,阻塞后判断线程状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// 获取锁失败 则取消尝试获取
cancelAcquire(node);
}
}
判断获取锁失败后是否需要阻塞
// AbstractQueuedSynchronizer
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
// 如果当前节点的前驱节点的等待状态为SIGNAL,则可以阻塞(返回true)
return true;
if (ws > 0) {
//如果当前节点的前驱节点的等待状态为CANCELLED,处于取消状态,则跳过前驱节点进行重试
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node; // 找到最近的状态不是取消的前驱节点 将该节点的后继节点设置为当前节点
} else {
/**
* 节点状态为0或PROPAGATE,表明需要一个信号,但不需要阻塞。需要重试确保在阻塞前不能获取锁
* CAS设置等待状态为SIGNAL
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// AbstractQueuedSynchronizer
private void cancelAcquire(Node node) {
if (node == null)
return;
node.thread = null;
// 跳过取消的前驱节点,找到最近的状态不为取消的前驱节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED;
// 如果当前节点是尾结点,则CAS设置当前节点的前驱节点为尾节点
if (node == tail && compareAndSetTail(node, pred)) {
// CAS设置当前节点的前驱节点的后继为null
compareAndSetNext(pred, predNext, null);
} else {
int ws;
// 判断 当前节点的前驱节点不是头节点 且 (等待状态为SIGNAL 或 等待状态不是取消状态,则设置等待状态为SIGNAL) 且 线程不为null
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
/**
* 如果当前节点的后继节点不为nul 且 等待状态不为取消状态
* 则CAS设置前驱节点的后继节点为当前节点的后继节点
*/
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 唤醒节点的后继节点
unparkSuccessor(node);
}
node.next = node; // 帮助GC
}
}
// AbstractQueuedSynchronizer
private void unparkSuccessor(Node node) {
/**
* 如果等待状态为负数,可能需要signal,尝试清除状态,
* 如果失败或被等待线程修改也没问题
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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)
// 唤醒该节点的线程
LockSupport.unpark(s.thread);
}
独占锁释放
// AbstractQueuedSynchronizer
public final boolean release(int arg) {
// 通过调用子类实现的该方法进行同步状态值得修改来释放独占锁
if (tryRelease(arg)) {
Node h = head;
// 头节点不为空 且 等待状态值不为0 则唤醒后继节点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享锁
共享锁获取
// AbstractQueuedSynchronizer
public final void acquireShared(int arg) {
// 尝试以共享方式获取锁
if (tryAcquireShared(arg) < 0)
// 尝试以共享不可中断模式获取
doAcquireShared(arg);
}
// AbstractQueuedSynchronizer
private void doAcquireShared(int arg) {
// 以给定模式构建节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true; // 获取共享锁是否失败标识
try {
boolean interrupted = false; // 中断标识
for (;;) {
final Node p = node.predecessor(); // 获取当前节点的前驱节点
if (p == head) { // 当前节点的前驱节点为头节点
int r = tryAcquireShared(arg); // 尝试获取共享锁
if (r >= 0) {
setHeadAndPropagate(node, r); // 获取共享锁成功 设置头节点并传播
p.next = null; // 帮助GC
// 中断标识为true 则中断当前线程
if (interrupted)
selfInterrupt();
failed = false; // 获取共享锁成功 设置获取共享锁失败标识为false
return;
}
}
// 判断再次获取锁失败后是否需要阻塞 如果需要,阻塞后判断线程状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// 获取锁失败 则取消尝试获取
cancelAcquire(node);
}
}
// AbstractQueuedSynchronizer
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node); // 设置当前节点为头节点
/**
* propagate大于0表示后续节点需要被唤醒
* 头节点为null或waitStatus<0,表示后继节点需要被唤醒
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 后继节点为null 或 后继节点为共享类型
if (s == null || s.isShared())
// 释放共享锁
doReleaseShared();
}
}
// AbstractQueuedSynchronizer
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 如果等待状态为SIGNAL,则CAS设置等待状态
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // CAS失败则循环重新检查
unparkSuccessor(h); // 唤醒后继节点
}
// 如果后续节点不需要唤醒 则将节点等待状态设置为PROPAGATE确保传播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // CAS失败则循环设置
}
// 头节点没有发生变化,结束循环,否则循环保证唤醒动作传播
if (h == head)
break;
}
}
共享锁释放
// AbstractQueuedSynchronizer
public final boolean releaseShared(int arg) {
// 尝试释放共享锁
if (tryReleaseShared(arg)) {
doReleaseShared(); // 释放共享锁
return true;
}
return false;
}
参考资料
Java并发编程的艺术
https://blog.csdn.net/heroqiang/article/details/79393466