AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch等等
源码
盗用一张图,来源http://www.cnblogs.com/waterystone/p/4920797.html

AQS通过维护一个private volatile int state,并提供getState(),setState(),compareAndSetState()等方法,让子类对共享变量state进行读写,从而实现以下方法:
1. tryAcquire:获取排它锁。
2. tryRelease: 释放排它锁。
3. tryAcquireShared:获取共享锁。
4. tryReleaseShared:释放共享锁。
5. isHeldExclusively:判断是否为排它策略。
以上5个方法在ReenterLock、ReadWriteLock、CountDownLatch等类中都有实现,到时候再具体分析。
首先来看下AQS中的一个重要数据结构,Node
static final class Node {
// 共享锁节点
static final Node SHARED = new Node();
// 排它锁节点
static final Node EXCLUSIVE = null;
//---------------下面很关键-----------
// CANCELLED表示该线程不参与竞争了,只有CANCELLED>0,也只有这个状态是表示没用的node
static final int CANCELLED = 1;
// 表示该节点的下一个节点需要被唤醒
static final int SIGNAL = -1;
// 表示该节点wait condition
static final int CONDITION = -2;
// 这个状态只有共享锁有,表示共享锁需要传播
static final int PROPAGATE = -3;
// 取值为CANCELLED、SIGNAL、CONDITION、PROPAGATE、0
// 0表示啥也不是,用于初始化
volatile int waitStatus;
/**
* 前驱指针,很有用,能够将状态为CANCELLED的节点跳过,直接挂到最近的SIGNAL节点后面
*/
volatile Node prev;
/**
* 用于唤醒后继节点
*/
volatile Node next;
// 记录该节点对于的线程
volatile Thread thread;
/**
* 下一个等待condition的节点
*/
Node nextWaiter;
// 省略一下方法
}
下来看AQS中的一下几个重要方法:
- acquire
/**
* 获取排它锁,忽略中断。方法会调用tryAcquire来获取锁,如果没有获取,则会将当前线程
* 添加到阻塞队列中,期间可能会被不断阻塞,唤醒并调用tryAcquire,直到tryAcquire获取到锁
*/
public final void acquire(int arg) {
// 先调用tryAcquire尝试获取一次,如果获取到锁,则返回true
// 如果tryAcquire返回false,则将当前线程加入阻塞队列,并不断尝试获取锁
// 如果在排队过程中被中断,那acquireQueued返回true,那么会执行selfInterrupt中断当前线程。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 如果
selfInterrupt();
}
/**
* 这个方法由子类实现,一般实现fair与unfair两种策略
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
/**
* 将当前线程与mode打包成一个Node,并添加到等待队列的队尾。
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 使用CAS尝试将当前节点添加到队尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果队列没有初始化或者添加失败,则调用enq方法
enq(node);
return node;
}
/**
* 这里使用经典的轮询CAS方式,将node添加到队尾。如果队列没有初始化,顺便初始化队列。
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // tail==null表示未初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
/**
* 为一个已经在等待队列中的Node获取排它锁
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取该节点的前驱节点
final Node p = node.predecessor();
// 如果前驱节点是head,并且获取到了锁,则将前驱节点删除
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 寻找休息点,即从当前节点往head的方向找,跳过所有waitStatus为CANCELLED的节点,直接link到最近的waitStatus为SIGNAL的节点后面。
// 找到休息点之后,将当前线程park起来,并返回Thread.interrupted()。
// 如果parkAndCheckInterrupt返回true,那将interrupted置位true。
// ********这里会被release唤醒,然后重新竞争
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果失败了,则将当前node从队列中移除。如果该node的next node挂到最近的有效pre node后面,跳过node到最近有效节点间的无效node(waitStatus>0的node)
if (failed)
cancelAcquire(node);
}
}
- release
/**
* 释放一个排它锁。
*/
public final boolean release(int arg) {
// 如果成功释放锁
if (tryRelease(arg)) {
// 获取head node,waitStatus == 0 表示空队列
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);// 唤醒下一个node
return true;
}
return false;
}
/**
* 由子类实现
*/
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
/**
* 唤醒node的后继节点
*
*/
private void unparkSuccessor(Node node) {
// 如果waitStatus<0,则先将node的waitStatus置成0,因为node是需要release的node
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
// 从tail往node遍历,找一个waitStatus<0的有效节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果找到了需要唤醒的节点,则唤醒。
// ********这里唤醒了之后,仍然需要争抢锁,如果没抢到,还是会继续等待下一次唤醒。
if (s != null)
LockSupport.unpark(s.thread);
}
- 3.
/**
* 获取共享锁
*
*/
public final void acquireShared(int arg) {
// 若果没获取到锁,tryAcquireShared返回值<0,则将当前线程加入等待队列
// 如果当前线程获取到了,但是没有其它线程可以获取了(理解为信号量用完了),tryAcquireShared返回0
// 如果当前线程获取到了,其它线程也可以获取(理解为信号量没用完),tryAcquireShared返回值>0
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
/**
* 尝试获取共享锁,由子类重写。
*/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
/**
* 在队列中获取共享锁
*/
private void doAcquireShared(int arg) {
// 将当前线程创建一个mode为SHARED的node
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);
// 如果获取到了,将p出队,使当前节点成为head,并让后继节点也尝试获取。这是与抢占式的区别。
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);
}
}
/**
* 将node设置为head,并且如果propagate>0,则继续唤醒后继节点
*/
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();// 唤醒后继节点
}
}
/**
* 唤醒后继节点,并传播下去
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//如果head的状态为SIGNAL,尝试将其状态变成0,并唤醒后继节点
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
//如果head的状态为0,则将其状态变成PROPAGATE
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)//head发送过改变,则循环;如果head不变,则break
break;
}
}
- releaseShared
/**
* 释放共享锁
*/
public final boolean releaseShared(int arg) {
// tryReleaseShared释放成功返回true
if (tryReleaseShared(arg)) {
doReleaseShared(); //执行子类重写的doReleaseShared方法
return true;
}
return false;
}
本文深入探讨了AbstractQueuedSynchronized(AQS)的源码,它是多线程同步的基础,用于实现如ReentrantLock、Semaphore、CountDownLatch等并发工具类。AQS维护了一个FIFO等待队列,并提供了独占和共享模式的获取与释放锁的方法。文章还提到了AQS的关键数据结构Node及acquire、release、releaseShared等核心方法。
2671

被折叠的 条评论
为什么被折叠?



