AQS和其衍生并发工具剖析
总览AQS
AQS: 是Java1.5提供的一个用来提供同步服务的框架,内部通过使用一个FIFO的CLH队列来进行多线程的互斥/协同操作,大体框架分为两种大模式EXCLUSIVE独占模式、SHARED共享模式,通过定义好模板方法,以及核心的同步/唤醒规则。方便我们自定义扩展同步并发工具。其中并发包中的ReentrantLock、Semaphore、CountDownLatch等,都是AQS扩展实现
框架流程
基本数据结构
- FIFO的双端队列
volatile state变量来存储状态信息 : 可以是重入锁数量、Permits数量等等。
核心API
只是列举常见的重要方法,可暂时略过,看到具体方法流程解析后,再看api。
核心模板方法
实际使用,可以直接调用模板方法进行资源(
state)修改
acquire(): 独享模式获取,与之对应的还有acquireInterrutibly()支持中断的获取release(): 独享模式释放acquireShared: 共享模式的获取 , 也有支持中断的版本acquireSharedInterrutiblyreleaseShared: 共享模式释放
子类扩展方法
tryXXX,其中XXX可为Acquire/Release等。都有对应共享模式和独享模式以及获取和释放模式。为子类实现,为具体的获取/释放规则。doXXXX,XXX可为acquire/release/acquireShared.., 是AQS中固定实现的获取失败/释放成功的回调方法,为核心的等待/唤醒逻辑。
相关辅助方法
addWaite(): 当获取锁失败的时候,添加到等待队列末尾逻辑shouldParkAfterFaildAcquire(): 核心判断是否需要等待逻辑 , 会将Node节点的waitStatus设置为SIGNAL,然后循环判断/等待parkAndCheckInterrupt: 调用LockSupport#part()进行等待hasQueuedPredecessors: 是否有线程正在等待cancelAcquire:取消获取acquireQueued: 核心等待逻辑
独享核心流程源码解析
acquire() 资源获取方法
public final void acquire(int arg) {
/**
* 1. tryAcquire() 独占模式获取锁,忽略中断
* - FairLock : 若无线程等待或自己重入则尝试获取锁
* - NonfairLock: 无线程获取锁/自己重入则重试获取锁
* 2. 获取锁失败,则被添加到等待队列末尾,并重新进行尝试获取
* - addWaiter() 添加到等待队列末尾,head节点为虚节点不存储数据
* - acquireQueued() 自旋/等待获取锁
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();/*若中断了,则重新设置中断标记*/
}
tryAcquire() : 子类实现,具体获取资源方法,下面用ReentrantLock举例
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { /*1. 若无线程获取锁,则判断是否有线程在等待获取锁(有没有则CAS尝试获取锁)*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} /*2. 若当前线程占锁则累加次数(重入)*/
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
比较简单 , 大体分两个流程
c==0,表示没有线程占锁,则判断是否有节点等待hasQueuedPredecessors(),这是公平锁的实现,而NonfairLock会直接CAS尝试获取锁
//这里写一大堆,其实就是判断 离head节点最近的 waitStatus<=0的节点是否为当前线程持有
//若不是则说明有线程在等待获取锁
public final boolean hasQueuedPredecessors() {
Node h, s;
if ((h = head) != null) {
if ((s = h.next) == null || s.waitStatus > 0) {
s = null; // traverse in case of concurrent cancellation
for (Node p = tail; p != h && p != null; p = p.prev) {
if (p.waitStatus <= 0)
s = p;
}
}
if (s != null && s.thread != Thread.currentThread())
return true;
}
return false;
}
currentThread == getExclusiveOwnerThread: 表示为重入锁,state+=acquire即可
锁获取失败 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter(): 构造节点,并将其添加到末尾,若为初始化则初始化head节点
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
//cas设置为tail节点
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
//初始化head节点
initializeSyncQueue();
}
}
}
acquireQueued(): 自旋获取锁/等待,并返回是否中断标志
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor(); /*1. 获取当前节点的前置节点*/
if (p == head && tryAcquire(arg)) { /*2. 判断是否是prev是否是头节点,是则尝试获取锁*/
setHead(node); /*3. 获取成功则设置为头结点并返回中断标记*/
p.next = null; // help GC
return interrupted;
} /*4. prev是头结点,但被其他锁抢占了(NonfairLock) 或者 prev不是头结点 若prev节点状态不是SIGNAL则设置为SIGNAL并返回false,其中会删除所有的CANCEL节点,若是SIGNAL则返回true*/
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();/*5. 对SIGNAL进行等待唤醒获取锁*/
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
release()释放资源方法
- 尝试释放锁,由子类实现
- 若释放锁成功,则将head.next唤醒 (唤醒下一个节点)
public final boolean release(int arg) {
/*1. 释放锁,若是当前线程,则减去对应state,state到0则释放锁成功*/
if (tryRelease(arg)) {
Node h = head;
/*这里h==null说明没有等待线程,h.waitStatus==0 只有当其他线程获锁失败,且acquireQueue()还没有进行等待时 (不需要唤醒)*/
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); /*2. 唤醒next线程*/
return true;
}
return false;
}
ReentrantLock#tryRelease()实现
//就是减去release,判断判断是否为0,为0则可以方式锁,否则返回false
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
unparkSuccessor() : 唤醒 node.next节点锁持有的线程,若状态非法,比如为null , 或者已经取消
这里从如果node.next 的waitStatus > 0 ,表示已经被取消,则从tail -> node 依次向前查找到最靠近node的合理唤醒节点进行
unpark()
private void unparkSuccessor(Node node) {
/*将当前节点waitStatus设置为0*/
int ws = node.waitStatus;
if (ws < 0)
node.compareAndSetWaitStatus(ws, 0);
/*为什么都是从tail向前遍历?因为addWaiter是非原子操作,极端情况可以只有prev指针相连*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null) /*获取队列最前方的节点进行唤醒*/
LockSupport.unpark(s.thread);
}
await() : 为Condition的具体等待实现
其实个人认为
AQS核心思想可以类比管程,管程也有多个条件变量,且每个变量有自己的等待队列,await()的时候加入等待队列并释放锁,signal的时候会将条件变量的firstWaite节点添加到同步队列(AQS的等待队列),unlock()的时候会唤醒同步队列中head.next线程,从而完成等待唤醒工作
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//1. 添加节点到CondtionObject#lastWriter
Node node = addConditionWaiter();
//2. 释放锁并唤醒其他线程
int savedState = fullyRelease(node);
int interruptMode = 0
//3. 当不在同步队列中(第一次肯定不在)则进行阻塞
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//4. 解锁之后尝试获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal
将当前
ConditionObject#firstWaiter添加到AQS同步队列中 , 使用enq(),并在解锁unlock()时唤醒
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//1. 获取firstWaiter
Node first = firstWaiter;
if (first != null)
//2. doSignal
doSignal(first);
}
private void doSignal(Node first) {
//将firstWaiter出队列
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
//help GC
first.nextWaiter = null;
//传送到同步队列中
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//若CAS修改状态失败则返回false
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
// 重点 : 加入到同步队列末尾,并返回tail节点
Node p = enq(node);
//若tail节点被取消,或者CAS设置为SIGNAL失败,则直接唤醒,否则,则等到unlock()的时候唤醒
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
共享核心流程源码解析
acquireShared()
public final void acquireShared(int arg) {
//1. 子类实现,获取permits,返回值有三种值类型
// 正值表示获取permits成功,并且还有剩余的permits
// 为0则表示获取成功,但是已经没有剩余的permits了
// 为负值表示 获取失败。
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
Semaphore#tryAcquireShared() : 信号量公平锁实现
protected int tryAcquireShared(int acquires) {
for (;;) {
//若同步队列中有节点正在等待则返回-1,表示获取失败
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
//返回剩余的permits,若剩余的不小于0则CAS修改
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
doAcquireShared() : 获取共享锁permits失败的操作
private void doAcquireShared(int arg) {
//1. 添加节点到tail
final Node node = addWaiter(Node.SHARED);
boolean interrupted = false;
try {
for (;;) {
//2. 和独占锁差不多的流程,都是判断prev是否是head,然后尝试获取permits
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
//3. 这里有锁不同,这里当获取的permits>=0时,也就是获取permits成功
if (r >= 0) {
//4. 将当前节点设置为head,当r >0 时还会唤醒node.next节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
return;
}
}
//5. 若不是head 或者 获取锁失败则进行阻塞
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
//6. 异常则取消当前获取操作并抛出异常
cancelAcquire(node);
throw t;
} finally {
//7. 若中断,则重新设置中断标志
if (interrupted)
selfInterrupt();
}
}
总结:其实无论是独占还是共享,都是以下几个步骤
- 通过子类实现的
tryXXx进行相关锁(permits或者其他关于state属性的操作)获取 - 获取失败则添加到阻塞队列,并进行等待
- release的时候,会通过
unparkSuccessor来唤醒
核心组件实现
ReentrantLock
其中实现为
独占模式,其中state代表重入的次数。通过AQS的acquire来完成互斥锁
//tryAcquire实现,其实就是简单的CAS修改state变量
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//1. 公平锁判断同步队列中是否有节点正在等待,没有则尝试CAS获取锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//2. 重入逻辑,累加state即可
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//tryRelease,若state减少到0,则释放锁。若不为0则释放失败
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
Semaphore
信号量是允许多个线程进入临界区的工具,所以必须是
共享模式实现
//tryAcquireShared
protected int tryAcquireShared(int acquires) {
for (;;) {
//公平锁实现,同步队列有节点等待则直接返回失败-1
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
//若remaining >=0则进行CAS设置,否则直接返回remaining
return remaining;
}
}
//tryReleaseShared,同样是CAS设置state就算成功
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
CountDownLatch
共享模式实现,用来在一个或多个线程中等待其他线程完成
其中初始化的时候,给
state设置了一定值countDown()方法则是通过复写tryReleaseShared来进行state的释放,当减到0的时候,进行唤醒await()方法则是用过tryAcquireShared能力来进行阻塞
//扩展AQS
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//1. await()方法,判断state值不为0即返回-1,会进入doAcquireShared进行等待
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//2. countDown()方法会减少state的值,当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;
}
}
}
CyclicBarrier
这个工具翻译为循环篱笆,是CountDownLatch的增强,一般使用场景在于等待多个线程执行完毕然后完成一定逻辑。且可以自动reset计数。底层是通过
ReentrantLock + Condition来完成,核心实现为await()
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock(); //1. 直接加锁
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//2. 计数为0,则执行传入的Runnable,并重置计数
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//2.1 重置计数并signalAll()唤醒所有等待线程
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//3. 计数不为0,则进行Condition等待
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
//相关异常和超时处理
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
ReentrantReadWriterLock
读写锁,其中
state的高16位读锁次数,低16位写锁次数。写锁为独占模式,读锁为共享模式读读不加锁,读写加锁,写写加锁,具体实现类似于
ReentrantLock,只是区分不同读写锁状态