目录
AQS概述
谈到并发,就会说到Lock,从而谈到ReentrantLock;而ReentrantLock就是基于AQS实现的。
AQS(AbstractQueuedSynchronizer),类如其名,抽象队列同步器,定义了一套多线程访问共享资源的同步器框架。
常用的ReentrantLock、Semaphore、CountDownLatch…就是基于它实现的。
框架
它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
-
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
-
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
-
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
-
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
-
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
源码分析
1、节点模式
public class AbstractQueuedSynchronizer {
static final class Node {
// 共享模式节点
static final Node SHARED = new Node();
// 独占模式节点
static final Node EXCLUSIVE = null;
}
}
2、节点状态waitStatus
public class AbstractQueuedSynchronizer {
static final class Node {
static final int CANCELLED = 1; // 已取消
static final int SIGNAL = -1; // 表示后继节点在等待当前节点唤醒
static final int CONDITION = -2; // 表示节点等待Condition,当condition的signal方法调用后,condition状态的节点将从等待队列到同步队列中
static final int PROPAGATE = -3; // 共享模式中,前置节点不仅会唤醒后继节点,还可能唤醒后继的所有节点
}
}
负值表示节点处于有效等待状态;而正值表示节点已经被取消;0表示新节点入队时的状态。
3、独占模式
使用acquire方法获取资源。
使用release方法释放资源。
accquire实现
accquire方法:实现类直接调用
public final void acquire(int arg) { // 获取资源
// 首先调用子类实现的tryAcquire方法尝试获取资源,如果获取失败,则添加节点入队尾,并在队列中排队
// acquireQueued返回线程是否中断过,如果中断过,则调用slefInterrupt设置中断
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
1、tryAcquire模板方法:留给子类实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
2、addWaiter方法:添加节点入队
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); // 否则调用enq添加
return node;
}
enq方法:添加节点
private Node enq(final Node node) {
for (;;) { // CAS自旋设置
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;
}
}
}
}
3、acquireQueued方法:在队列中阻塞排队
final boolean acquireQueued(final Node node, int arg) { // 在等待队列中排队
boolean failed = true; // 是否获取失败
try {
boolean interrupted = false; // 是否有中断
for (;;) {
final Node p = node.predecessor(); // 获取node的前置节点
if (p == head && tryAcquire(arg)) { // 如果前驱节点为head,那么有资格去获取资源
setHead(node); // 获取资源后将head指向该节点
p.next = null; // help GC
failed = false; // 成功获取资源
return interrupted; // 返回等待过程中是否被中断
}
if (shouldParkAfterFailedAcquire(p, node) && // 检查前驱节点的状态
parkAndCheckInterrupt()) // 阻塞在此并检查中断
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
acquireQueued方法:检查前驱节点是否可以跳过
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); // 如果前驱正确,则将其状态设置为SIGNAL,告诉它拿完号后通知一下自己
}
return false;
}
parkAddCheckInterrupt方法:阻塞并检查其是否中断
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 调用park()使线程进入waiting状态
return Thread.interrupted(); // 返回线程是否中断,并且清除其中断标识位
}
release实现
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;
}
unparkSuccessor方法:唤醒后续节点
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;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 设置节点状态为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) { // 如果为null或者已经取消
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); // 唤醒线程
}
4、共享模式
调用acquireShared获取共享资源。
调用releaseShared释放共享资源。
acquireShared
acquireShared方法:获取共享资源
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) // 尝试获取共享资源
doAcquireShared(arg); // 执行获取共享资源操作
}
doAcquireShared方法:执行获取共享资源的操作
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; // help GC
if (interrupted) // 如果有中断则调用中断
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && // 检查前驱节点状态
parkAndCheckInterrupt()) // 阻塞线程和检查中断
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate方法:
设置头和传播后置节点。
会调用doReleaseShared尝试去唤醒新的头结点。
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) { // 如果还有剩余量,继续唤醒后面的线程
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared(); // 执行释放共享资源的操作
}
}
doReleaseShared方法:
执行释放共享资源的操作。当头结点未发生改变时跳出循环。
如果其中有acquire线程被唤醒,头结点发生了改变,则不会跳出循环;
保证了某个循环的过程中有线程刚获取到了锁并设置了新head,可以唤醒新的头节点。
private void doReleaseShared() { // 释放共享资源
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head; // 获取头结点
if (h != null && h != tail) { // 如果头结点不为null并且不等于尾节点
int ws = h.waitStatus; // 获取头结点的状态
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h); // 如果CAS设置成功则唤醒该节点
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) // 如果当前状态为0,并且设置status为PROPAGATE失败
continue; // loop on failed CAS
}
if (h == head) // 如果被 acquire被唤醒,head会改变,则不会跳出循环
break;
}
}
releaseShared
releaseShared方法:释放共享资源,直接调用doReleaseShared方法。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { // 尝试释放共享资源
doReleaseShared(); // 释放共享资源
return true;
}
return false;
}