AQS,AbstractQueuedSynchronizer的缩写,是JUC中非常重要的一个类。javadoc中对其的介绍是:
为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。
AbstractQueuedSynchronizer是CountDownLatch、ReentrantLock、RenntrantReadWriteLock、Semaphore等类实现的基础。在这几个类中都有这样名为Sync的一个内部类,比如在ReentrantLock中:
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
Sync的主要目的是作为一个同步器,同步器中必须定义更改状态的受保护方法,并定义哪种状态对此同步器意味着被获取或者被释放。完成这些后,Sync中的其他方法就可以实现所有的排队和阻塞机制。
结构示意图
下图是AQS的结构示意图:
如图所示,AQS维护了一个表示状态的STATE和一个FIFO线程等待队列。
STATE
STATE是共享资源,描述当前有多少线程获取了锁,对于互斥锁来说state<=1。。在源码中对应的属性为:
private volatile int state;
通过使用getState()、setState(int) 和/或 compareAndSetState(int, int) 方法可以检查和/或修改同步状态。
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
AQS支持两种资源访问方式,独占模式(Exclusive)和共享模式(Share)。通常,AQS的子类只支持其中一种模式,但也有例外,如ReadWriteLock支持两种模式。只支持独占模式或者只支持共享模式的子类不必定义支持未使用模式的方法。
为了将此类用作同步器的基础,子类需要适当地重新定义以下方法:
- tryAcquire(int)。在独占模式下获取对象状态。
- tryRelease(int)。设置状态来反映独占模式下的一个释放。
- tryAcquireShared(int)。在共享模式下获取对象状态。
- tryReleaseShared(int)。设置状态来反映共享模式下的一个释放。
- isHeldExclusively()。如果对于当前线程,同步是以独占方式进行的,则返回 true。
在AQS中,这几个方法都抛出UnsupportedOperationException。
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的子类(锁或者同步器)需要进行重写这几个方法实现共享资源STATE的获取与释放方式。以ReentrantLock为例,ReentrantLock是独占锁,只有独占模式(Exclusive)一种资源访问方式,所以在NonfairSync和FairSync中只有tryAcquire(int)和tryRelease(int)方法。state初始化为0,表示未锁定状态。线程thread调用reentrantLock.lock()时,会调用tryAcquire(1)独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到线程thread调用unlock()时,会调用tryRelease将state-1,这时state=0,这时其它线程才有机会获取该锁。当然,ReentrantLock是可重入的锁,在释放锁之前,线程thread自己是可以多次调用lock()重复获取此锁的,state会累加。释放锁时要重复调用unlock()释放锁,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
FIFO线程等待队列
CHL队列是一个非阻塞的FIFO队列,通过自旋锁和CAS保证节点插入和移除的原子性。下面是队列的头结点和尾节点。
/**
* CHL头节点
*/
private transient volatile Node head;
/**
* CHL尾节点
*/
private transient volatile Node tail;
Node是队列的节点类。
static final class Node {
/** 表示节点处于共享模式 */
static final Node SHARED = new