AbstractQueuedSynchronizer(AQS)提供了一个框架,用于实现依赖于“先进先出”(FIFO)等待队列的阻塞锁和相关同步器(semaphore,events等)。AQS解决了在实现同步器时涉及的大量细节问题,例如等待线程采用FIFO队列操作顺序。在不同的同步器中还可以定义一些灵活的标准来判断某个线程是应该通过还是需要等待。基于AQS来构建同步器不仅能极大减少实现工作,而且不必在多个位置上发生竞争问题。
在基于AQS构建的同步器类中,最基本的操作包括各种形式的获取操作和释放操作。获取操作是一种依赖于状态的操作,并且通常会阻塞。对于锁或者信号量(semaphore)而言,获取意味着获取锁或者许可,并且调用者可能会一直等待直到同步器类处于可获取的状态。对于CountDownLatch而言,获取意味着“等待并直到闭锁到达结束状态”。而对于FutureTask而言,则意味着“等待并直到任务已经完成”。释放并不是一个可阻塞的操作,当执行释放操作时,所有在请求时被阻塞的线程都会开始执行。
根据同步器的不同,获取操作可以是独占的(ReentrantLock),也可以是共享的(Semaphore和CountDownLatch)。一个获取操作包括两个部分:首先,同步器判断当前状态是否允许获取操作,如果允许则线程执行,否则获取操作将被阻塞或失败,这种判断是由同步器语义来决定的;其次,就是更新同步器的状态。
AQS通过使用一个int值来管理同步器类中的状态,可以通过getState(),setState()以及compareAndSetState方法来进行原子更新状态值。这个int值可以表示任意状态,例如,ReentrantLock中表示所有者线程已经重复获取该锁的次数,Semaphore用它来表示剩余的许可数量,FutureTask用它来表示任务的状态(尚未开始/正在运行/已完成/已取消)。在同步器类中还可以管理一些额外的状态变量,例如,ReentrantLock保存了锁的当前所有者的信息,这样就能区分某个获取操作是重入的还是竞争的。
这个类的序列化仅仅存储底层状态,所以反序列化对象中线程队列为空,通常子类要求序列化时定义一个readObject方法用来恢复初始状态。
一个基于AQS实现的简单闭锁:
public class OneShotLatch {
private final Sync sync = new Sync();
public void signal() {
sync.releaseShared(0);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(0);
}
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected int tryAcquireShared(int ignored) {
return (getState() == 1) ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int ignored) {
setState(1);
return true;
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0);
}
}
}
OneShotLatch是一个使用AQS实现的二元闭锁,它包含两个共有方法:await和signal方法,分别对应获取操作和释放操作。其中,AQS状态用来表示闭锁状态——关闭(0)或者打开(1)。