AQS全称是AbstractQueuedSynchronizer,是JDK提供的一个同步器设计框架,很多并发数据结构如ReentrantLock、ReentrantReadWriteLock、Semaphore等都是基于AQS来实现的,下面来分析一下AQS的原理。
AQS底层维护了一个state(代表共享资源)和一个CLH队列(代表线程等待队列)
state:state是一个volatile int型变量,用以表示共享资源的使用情况。
head、tail:分别表示CLH队列的头节点和尾节点。关于CLH队列原理可以看这篇博客:
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
AQS定义了两种资源:
一种是Exclusive(独占方式,同一时间只有一个线程能够访问,比如ReentrantLock);
一种是Share(共享方式,同一时间允许多个线程访问,比如Semaphore)。
AQS被定义为一个同步器框架,那么为什么说AQS是一个“框架”呢,框架的定义应该是使用者根据框架的要求去实现一些业务逻辑,然后底层由框架自己去完成,比如使用Hadoop大数据框架的时候,我们只需要定义好Map/Reduce函数就可以了,其他复杂的底层操作就有Hadoop帮我们完成。AQS也应该是这样一个东西,事实上,AQS也确实是这样做的,它只需要开发者实现其中的几个函数,其他的底层操作比如CLH队列操作、CAS自旋等等都是由AQS框架来实现。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
上面的几个方法就是AQS要我们实现的函数,这几个方法都是AQS提供给开发者编写代码逻辑的,因此每个方法里面都定义为直接抛出UnsupportedOperationException,下面解释一下这几个函数的作用。
tryAcquire:尝试获取独占资源。成功返回true,失败返回false。
tryRelease:尝试释放独占资源。成功返回true,失败返回false。
tryAcquireShared:尝试获取共享资源。成功返回一个非负数,代表剩余资源(0就表示没有可用资源),负数表示失败。
tryReleaseShared:尝试释放共享资源。成功返回true,失败返回false。
isHeldExclusively:判断线程是否正在独占资源。
对于独占方式的同步器,只需要实现tryAcquire-tryRelease;而对于共享方式的同步器则只需要实现tryAcquireShared-tryReleaseShared就行了,当然如果自定义同步器需要同时实现独占和共享方式的话也可以,比如读写锁ReentrantReadWriteLock,其中读锁是共享的,写锁是独占的。
AQS中对于资源获取的顶层接口是acquire-release和acquireShare-releaseShare,下面从这些顶层接口来分析:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
代码逻辑:
(1)尝试获取资源(tryAcquire),成功则返回,失败则跳转到(2)
(2)请求将该线程加入线程等待队列的尾部,并标记为独占模式(addWaiter(Node.EXCLUSIVE)),并不断请求队列智斗获取到资源,如果该过程被打断,则跳转到(3),失败则返回。
(3)自我中断(selfInterrupt)
(4)完成
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
代码逻辑:
(1)尝试释放资源,如果成功则跳转到(2),失败则返回false
(2)获取线程等待队列的头节点,如果头节点线程不为空且处于等待状态,则唤醒该线程(unparkSuccessor)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
代码逻辑:
(1)尝试获取共享资源,如果获取失败则调用doAcquireShared自旋来请求共享资源。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
代码逻辑:
(1)尝试释放共享资源,释放成功则唤醒等待队列中的后继节点来获取资源。
5、如果需要响应中断程序的话,AQS还提供了acquireInterruptibly和acquireSharedInterruptibly函数来响应。
一般用AQS来实现同步器主要使用内部类的方式来实现的,ReentrantLock里面就是一个比较经典的实现,看一下这个内部类的实现源码:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
@ReservedStackAccess
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;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
这里定义的Sync同步器类仍然是一个抽象类,是因为ReentrantLock同时支持公平锁和非公平锁,而这两种同步方式是不一样的,所以先定义一个Sync类作为两种实现的基类,后面会看到具体的公平锁和非公平锁的实现。
由于ReentrantLock本身是一个独享锁,因此Sync类主要重写了AQS里面的tryRelease和isHeldExclusively方法,同时定义了非公平锁的nonfairTryAcquire方法作为请求资源的方法。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
其实基类Sync完完全全就是一个非公平锁的实现,所以NonfairSync并没有新代码,tryAcquire直接调用nonfairTryAcquire。
static final class FairSync extends Sync {
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
公平锁与非公平锁的区别也就是资源请求的方式不一样,所以在基类Sync的基础上重写tryAcquire方法,顺序唤醒等待队列中的线程。
基于AQS实现了公平锁和非公平锁之后,如何实现上锁和解锁的功能也就十分简单了,源码直接用一行代码就实现了:
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}