AQS 源码分析
什么是 AQS
AQS,全称是 AbstractQueuedSynchronizer,是位于 java 并发包中的一个抽象类,本文使用的版本是 1.8
该类的作用,用开头的一段注释就可以很好的表达
/**
* Provides a framework for implementing blocking locks and related
* synchronizers (semaphores, events, etc) that rely on
* first-in-first-out (FIFO) wait queues. This class is designed to
* be a useful basis for most kinds of synchronizers that rely on a
* single atomic {@code int} value to represent state
翻译过来就是:此类提供了实现阻塞锁和同步的框架,基于 FIFO 队列来实现。此类被设计为是一个基础的同步器,通过一个原子性的 int 值来表示同步器的状态
AQS 实现了两种模式,独占模式和共享模式,分别用于实现独占锁和共享锁
以独占模式为例,队列中头节点表示已经获取到了锁,其他节点都处于等待状态。释放锁之后,头节点的下一个节点会尝试去获取锁,但并不意味着一定会成功,如果失败将会继续等待
内部类 Node
AQS 有两个内部类:Node 和 ConditionObject,这里只介绍 Node 类
Node 类是等待队列中的节点类,是一个基于 FIFO 的双向队列
主要的属性及方法如下
/** * SIGNAL: 当前节点的线程完成或者取消后, 需要设置为此状态以便唤醒后节点的线程 * CANCELLED: 节点会被取消如果获取锁超时或者线程被打断, 设置为此状态的节点不会再改变其状态 * CONDITION: 表示节点处于 Condtion 队列中, 不会被用作同步队列节点 * PROPAGATE: 用于共享模式下的传播释放等待线程 * 0: 初始值 * * 通过 CAS 来改变属性值 */ volatile int waitStatus; /** * 指向前节点 * 当前节点被取消时, 需要指向新的未被取消的节点作为前节点, * 头节点不会被取消:因为节点成为头节点就意味着成功获取到了锁, 一个被取消的节点永远不会获取到锁 */ volatile Node prev; /** * 指向后节点, 当后节点被取消时需要指向新的节点或者空 */ volatile Node next; /** * 入队节点的线程, 结束之后会被置空 */ volatile Thread thread; /** * * Condition 队列中的下一个等待节点 */ Node nextWaiter;
主要属性
AQS 主要属性说明如下
/** * 等待队列的头节点, 赖加载 * 除了初始化之外, 只能通过 setHead 方法来改变其值 * 如果 head 不为 null, waitStatus 值就一定不会是 CANCELLED */ private transient volatile Node head; /** * 等待队列的尾结点, 懒加载 * 只能通过 enq 方法添加新节点时才会去改变尾结点 */ private transient volatile Node tail; /** * 同步器的状态 * 以 ReentrantLock 为例, 0 表示可以获取到锁, 其他的正整数表示无法获取到锁 */ private volatile int state;
方法
这里主要分析获取和释放锁相关的代码
获取锁
1)acquire
获取独占锁,方法的逻辑是先尝试获取锁,如果失败,就往队列末尾添加一个节点,再尝试从队列里获取锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
2)tryAcquire
尝试获取独占锁,需要由子类自己实现,体现了 Abstract
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
3)addWaiter
往尾部添加一个节点作为新的尾结点。如果尾结点未初始化,则通过 enq 完成初始化
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;
// 通过 cas 将 tail 指向新的节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 完成尾结点初始化和设置的工作
enq(node);
return node;
}
4)acquireQueued
不断尝试从队列中获取锁,当成功获取到锁或者线程被打断时会成功退出循环,竞争锁失败的线程会等待直到被唤醒,唤醒之后会再次进入循环尝试去获取锁,不断的重复整个过程
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; // help GC
failed = false;
return interrupted;
}
// 获取锁失败后进到这里
// 先判断线程是否需要 park, 如果是就执行 park, 否则再进入循环
// 这里还判断了线程是否已经被打断了, 如果是, 就会执行 cancelAcquire
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5)acquireShared
获取共享锁,方法逻辑是先尝试获取锁,如果失败,就不断尝试去获取锁
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
6)tryAcquireShared
尝试获取共享锁,需要由子类自己实现
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
7)doAcquireShared
不断尝试从队列中获取共享锁,当成功获取到锁或者线程被打断时会成功退出循环,竞争锁失败的线程会被 park 直到被唤醒,唤醒之后会再次进入循环尝试去获取锁,不断的重复整个过程
private void doAcquireShared(int arg) {
// 在队列末尾加入节点, 模式为 SHARED
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;
}
}
// 判断是否需要 park 以及线程是否被打断
// 同 acquireQueued 方法
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
释放锁
1)release
释放独占锁,方法逻辑是首先尝试释放锁,成功后,如果头节点不为空而且 waitStatus 不为 0(即节点不是初始化的状态),就唤醒后节点的线程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
2)tryRelease
尝试释放独占锁,需要由子类实现
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
3)releaseShared
是否共享锁,逻辑是先尝试释放锁,如果成功,再依次去释放节点上阻塞的线程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
4)tryReleaseShared
尝试释放共享锁,需要由子类实现
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
5)doReleaseShared
释放共享锁
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) {
int ws = h.waitStatus;
// 如果 waitStatus 为 SIGNAL, 表示可以去释放锁
if (ws == Node.SIGNAL) {
// 通过 cas 将 waitStatus 设为 0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 唤醒当前节点后节点的线程
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
共占锁和共享锁的异同
相同点
1)获取锁前都会判断是否有权限,只有满足条件才可能获取到锁
2)未获取到锁的线程会创建新节点放入队列尾部
不同点
1)独占锁只会释放头部后节点的线程,而共享锁会依次释放所有线程
2)独占锁存在非公平锁的情况,新的线程可能抢占队列中线程的锁,共享锁则不存在这种情况
子类实现
ReetrankLock
ReetrankLock 中的 Sync 内部类实现了 AQS,是一种独占锁,下面来看下它对 AQS 的实现(以 NonfairSync 为例)
1)tryAcquire
对 AQS 中 tryAcquire 的实现
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// state == 0 表示此线程有权限去获取锁
if (c == 0) {
// 通过 CAS 保证只有一个线程可以顺利获取到锁
if (compareAndSetState(0, acquires)) {
// 设置 exclusiveOwnerThread 为当前线程, 用于实现可重入
setExclusiveOwnerThread(current);
return true;
}
}
// 这里表示获取锁的是当前线程, 提供可重入功能
else if (current == getExclusiveOwnerThread()) {
// 用于 state 计数, 每次 unlock 之后都会减 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
2)tryRelease
对 AQS 中 tryRelease 的实现
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// state == 0 表示锁完全释放, 此时别的线程可以去争夺锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
CountDownLatch
CountDownLatch 中的 Sync 内部类实现了 AQS,是一种共享锁,下面来看下它对 AQS 的实现
1)tryAcquireShared
state 为 0 时表示可以获取到锁
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
2)tryReleaseShared
state 最终为 0 表示释放锁成功
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
// 此时不需要去释放锁
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}

浙公网安备 33010602011771号