简单介绍
AbstractQueuedSynchronizer这个词拆分解释就是抽象队列同步器的意思,一般简称AQS。Java许多并发工具类的内部实现都依赖于它,最常见的比如:ReentrantLock、CountDownLatch、Semaphore。
AQS的主要使用方式是继承它作为一个内部辅助类实现同步原语,像上面提到的几个并发工具类,其都包含了一个内部类Sync继承AQS来实现具体获取锁和释放锁的逻辑。它可以简化你的并发工具的内部实现,屏蔽同步状态管理、线程的排队、等待与唤醒等底层操作。
实现思路:
AQS内部维护一个CLH队列来管理锁。
线程会首先尝试获取锁,如果失败,则将当前线程以及等待状态等信息包成一个Node节点加到同步队列里。
接着会不断循环尝试获取锁(条件是当前节点为head的直接后继才会尝试),如果失败则会阻塞自己,直至被唤醒;
而当持有锁的线程释放锁时,会唤醒队列中的后继线程。
AQS结构:
可以看下AbstractQueuedSynchronizer的主要属性、内部类以及原子类方法:
// 链表头结点
private transient volatile Node head;
// 链表尾节点
private transient volatile Node tail;
// 线程获取锁的状态,默认为0代表还没有线程获取到锁,1代表有一个线程获取到了一次锁,大于1代表线程获取到了多次锁(重入锁的实现)
private volatile int state;
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
// 表示当前的线程被取消
// 当前线程所在节点的前继节点内线程状态为此值时,当前节点内线程可值机尝试获取锁而不用阻塞
static final int CANCELLED = 1;
// 表示当前节点的后继节点包含的线程需要运行,也就是unpark
static final int SIGNAL = -1;
// 表示当前节点在等待condition,也就是在condition队列中
static final int CONDITION = -2;
// 表示当前场景下后续的acquireShared能够得以执行
static final int PROPAGATE = -3;
// 默认值为0,表示当前节点在sync队列中,等待着获取锁
volatile int waitStatus;
// 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接
volatile Node prev;
// 后继节点
volatile Node next
// 存储condition队列中的后继节点
volatile Thread thread;
// 入队列时的当前线程
Node nextWaiter;
}
// 原子性的更新state
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 原子性的更新头结点
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
// 原子性的更新尾节点
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
// // 原子性的更新节点等待状态
private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);
}
// // 原子性的更新指定节点的下一个节点
private static final boolean compareAndSetNext(Node node,Node expect,Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
获取锁的实现逻辑:
// 获取锁的起始方法,在ReentrantLock中由lock()方法调用
public final void acquire(int arg) {
// 先尝试获取锁,是否失败?
if (!tryAcquire(arg) &&
// addWaiter(Node.EXCLUSIVE):将当前线程构造成一个队列放入道CLH队列尾部
// 在CLH队列中会检测当前线程构造的节点是否为head的直接后继,并尝试获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 中断当前线程
selfInterrupt();
}
// 尝试获取锁状态,这是实际获取锁的实现逻辑。
// 这个由具体子类实现获取锁状态的逻辑,比如在ReentrantLock中有FairSync(公平)和NonfairSync(非公平,默认)的不同实现。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 在CLH队列中会检测当前线程构造的节点是否为head的直接后继,并尝试获取锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 当前线程构建的节点是否有前置节点,没有则抛出NullPointerException
final Node p = node.predecessor();
// 是否是头结点并且获取锁状态成功
if (p == head && tryAcquire(arg)) {
// 将当前线程构建的节点设置为头结点
setHead(node);
// 将原本头结点的后继节点引用消除,用来回收原头结点
p.next = null; // help GC
failed = false;
return interrupted;
}
// 在这里需要根据当前线程构建节点的前继节点waitStatus状态来判断是否要阻塞。
// 1、前驱节点为SIGNAL状态,在释放锁的时候会唤醒后继节点,所以后继节点(也就是当前节点)现在可以阻塞自己。
// 2、前驱节点为CANCEL,向前遍历,更新当前节点的前驱为往前第一个非取消节点,当前节点会会再次回到循环并尝试获取锁。
// 3、等待状态为0或者PROPAGATE,设置前驱的等待状态为SIGNAL,并且之后会回到循环再次重试获取锁。
// 如果未成功获取锁则根据前驱节点判断是否要阻塞
// 如果阻塞过程中被中断,则置interrupted标志位为true
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 将当前线程构造成一个队列放入道CLH队列尾部
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);
return node;
}
// 初始情况或者在快速尝试失败后插入节点
private Node enq(final Node node) {
for (;;) {
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;
}
}
}
}
释放锁的实现逻辑:
// 释放锁的起始方法,在ReentrantLock中由unlock()调用
public final boolean release(int arg) {
// 释放锁,释放成功?
if (tryRelease(arg)) {
// 若释放锁成功,先获取头结点的引用
Node h = head;
// 如果头结点不为空切头结点的waitStatus不等于0
if (h != null && h.waitStatus != 0)
// 唤醒后继结点包含的线程
unparkSuccessor(h);
return true;
}
return false;
}
// 是否锁的逻辑,实际有子类去做不同的实现
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 唤醒后继节点包含的线程
private void unparkSuccessor(Node node) {
// 获取节点的等待状态
int ws = node.waitStatus;
// 是否小于0
if (ws < 0)
// 将其更新为0
compareAndSetWaitStatus(node, ws, 0);
// 获取节点的后继节点
Node s = node.next;
// 若后继节点为null 或 后继节点的等待状态为CANCEL(大于0就只能是CANCEL)
if (s == null || s.waitStatus > 0) {
s = null;
// 从尾节点开始向前遍历
for (Node t = tail; t != null && t != node; t = t.prev)
// 如果节点的等待状态不为CANCEL
if (t.waitStatus <= 0)
// 将参数节点的后继节点设为此节点
s = t;
}
// 若参数节点的后继节点为null
if (s != null)
//
LockSupport.unpark(s.thread);
}