目录
AQS 概述
AbstractQueuedSynchronizer来自于jdk 1.5,位于juc包中,简称为AQS- 类如其名,抽象的队列式的同步器,
AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock,ReentrantReadWriteLock,CountDownLatch - 在
AQS中,主要有两部分功能:一部分是操作state变量,第二部分是实现排队和阻塞机制
AQS 设计思想
对于使用者来讲,我们无需关心获取资源失败,线程排队,线程阻塞,唤醒等一系列复杂的实现,这些都在 AQS 中为我们处理好了。我们只需要负责处理获取,释放锁资源的状态 state的逻辑即可。这是很经典的模板方法设计模式的应用,AQS 为我们定义好顶级逻辑的骨架,并提取出公用的线程入队列,出队列,阻塞,唤醒等一系列复杂逻辑的实现,将部分简单的可由使用者决定的操作逻辑放到 AQS 的子类中去实现即可
AQS 框架内部探究
AQS 框架内部

AbstractQueuedSynchronizer被设计为一个抽象类,它使用了一个volatile来修饰int类型的成员变量state来表示同步状态,通过内置的FIFO(先进先出)双向队列来完成资源获取线程的排队等待工作。通常AQS的子类通过继承AQS并实现它的抽象方法来管理同步状态AQS自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法,AQS既可以支持独占式地访问同步状态( 如ReentrantLock),也可以支持共享式地访问同步状态(如CountDownLatch),这样就可以方便实现不同类型的同步组件
AQS 访问同步状态 state
重写 AQS 指定的方法时,需要使用 AQS 提供的如下 3 个方法来访问或修改同步状态,不同的锁实现都可以直接调用这 3 个方法
// 同步状态变量,或者代表共享资源
private volatile int state;
// 返回同步状态的当前值。此操作具有 volatile 读的内存语义,因此每次获取的都是最新值
protected final int getState() {
return state;
}
// 设置同步状态的最新值。此操作具有 volatile 写的内存语义,因此每次写数据都是写回主内存
protected final void setState(int newState) {
state = newState;
}
/**
* 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的要更新的值
* * @param expect 预期值
* @param update 写入值
* @return 如果更新成功返回true,失败则返回false
*/
protected final boolean compareAndSetState(int expect, int update) {
// 内部调用 unsafe 的方法,该方法是一个 CAS 方法
// 这个 unsafe 类,实际上是比 AQS 更加底层的底层框架,或可以认为是 AQS 框架的基石
// CAS 操作在 Java 中的最底层的实现就是 Unsafe 类提供的,它是作为 Java 语言与 Hospot源码(C++)以及底层操作系统沟通的桥梁
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
这三个方法 getState()、setState()、compareAndSetState() 都是 final 方法,是 AQS 提供的通用的访问同步状态的方法,能保证线程安全,我们直接调用即可
AQS 的自定义(子类)同步器的实现
AQS定义了两种资源享用方式:Exclusive(独占,如ReentrantLock)和Share(共享,如CountDownLatch)- 不同的自定义同步器竞争使用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源
state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队,唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法
/**
* 独占式获取锁,该方法需要查询当前状态并判断锁是否符合预期,然后再进行 CAS 设置锁。返回true 则成功,否则失败
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
/**
* 独占式释放锁,等待获取锁的线程将有机会获取锁。返回 true 则成功,否则失败
*/
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
/**
* 共享式获取锁,返回大于等于 0 的值表示获取成功,否则失败
*
* 如果返回值小于 0,表示当前线程获取共享锁失败
* 如果返回值大于 0,表示当前线程获取共享锁成功,并且接下来其他线程尝试获取共享锁的行为很可能成功
* 如果返回值等于 0,表示当前线程共享锁成功,但是接下来其他线程尝试获取共享锁的行为会失败(实际上也有可能成功,在后面的源码部分会将)
*/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
/**
* 共享式释放锁。返回 true 成功,否则失败
*/
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
/**
* 当前 AQS 是否在独占模式下被线程占用,一般表示是否被前当线程独占;
* 如果同步是以独占方式进行的,则返回 true;其他情况则返回 false
*/
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
AQS 中同步队列
数据结构之队列
- 队列
queue是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列的工作原理与现实生活中的队列完全相同。类似火车头进入山洞,先进入山洞的车厢就先出来山洞,后进入山洞的火车头后出来山洞。队列的工作原理与此相同 - 队列是一种先进先出(
First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。假设队列是q = (a1,a2,…,an),那么a1就是队头元素,而an是队尾元素。这样我们就可以删除时,总是从a1开始,而插入时,列在最后

因为队列属于线性表,因此队列也可以采用顺序存储结构和链式存储结构来实现。Java 中已经提供了很多线程的队列的实现,比如 JUC 中的各种阻塞、非阻塞队列
AQS 中同步队列数据结构(双链表实现的队列)
public abstract class AbstractQueuedSynchronizer extends
AbstractOwnableSynchronizer implements java.io.Serializable {
protected AbstractQueuedSynchronizer() {
}
// 队列头结点,实际上是一个哨兵结点,不代表任何线程,head 所指向的 Node 的 thread 属性永远是 null
private transient volatile Node head;
// 队列尾结点,后续的结点都加入到队列尾部
private transient volatile Node tail;
// 同步状态
private volatile int state;
// Node内部类,同步队列的结点类型
static final class Node {
// 共享模式下构造的结点,用来标记该线程是获取共享资源时被阻塞挂起后放入AQS 队列的
static final Node SHARED = new Node();
// 独占模式下构造的结点,用来标记该线程是获取独占资源时被阻塞挂起后放入AQS 队列的
static final Node EXCLUSIVE = null;
/**
* 表示当前结点(线程)需要取消等待
* 由于在同步队列中等待的线程发生等待超时、中断、异常,即放弃获取锁,需要从同步队列中取消等待,就会变成这个状态
* 如果结点进入该状态,那么不会再变成其他状态
*/
static final int CANCELLED = 1;
/**
* 表示当前结点(线程)的后续结点(线程)需要取消等待(被唤醒)
* 如果一个结点状态被设置为SIGNAL,那么后继结点的线程处于挂起或者即将挂起的状态
* 当前结点的线程如果释放了锁或者放弃获取锁并且结点状态为SIGNAL,那么将会尝试唤醒后继结点的线程以运行
* 这个状态通常是由后继结点给前驱结点设置的。一个结点的线程将被挂起时,会尝试设置前驱结点的状态为SIGNAL
*/
static final int SIGNAL = -1;
/**
* 线程在等待队列里面等待,waitStatus值表示线程正在等待条件
* 原本结点在等待队列中,结点线程等待在Condition上,当其他线程对Condition调用了signal()方法之后
* 该结点会从从等待队列中转移到同步队列中,进行同步状态的获取
*/
static final int CONDITION = -2;
// 共享模式下,前继结点不仅会唤醒其后继结点

AQS(AbstractQueuedSynchronizer)是Java中一个抽象的队列同步器,用于实现并发组件如ReentrantLock、Semaphore等。AQS使用一个int类型的成员变量表示同步状态,并通过内置的FIFO同步队列来完成资源获取线程的排队等待。AQS提供了一套模板方法,使用者只需关注同步状态的获取和释放,而复杂的线程入队、出队、阻塞和唤醒等操作由AQS处理。AQS支持独占式和共享式访问同步状态,子类通过重写其提供的方法来实现具体的同步逻辑。
2302

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



