AQS解释
1.AQS:AbstractQueuedSynchronizer,是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态。
2.AQS 用状态属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
- 独占模式是只有一个线程能够访问资源,如 ReentrantLock
- 共享模式允许多个线程访问资源,如 Semaphore,ReentrantReadWriteLock 是组合式。
AQS核心思想
-
如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置锁定状态
-
请求的共享资源被占用,AQS 用队列实现线程阻塞等待以及被唤醒时锁分配的机制,将暂时获取不到锁的线程加入到队列中
CLH 是一种基于单向链表的高性能、公平的自旋锁,AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。
AQS中的队列是CLH变体的虚拟双向队列FIFO
和AQS有关的
- 独占模式是只有一个线程能够访问资源,如 ReentrantLock
- 共享模式允许多个线程访问资源,如 Semaphore,CyCountDownLatch,ReentrantReadWriteLock 是组合式
设计原理
1.AQS自身state设计
state 使用了 32bit int 来维护同步状态,独占模式 0 表示未加锁状态,大于 0 表示已经加锁状态
state 使用 volatile 修饰配合 cas 保证其修改时的原子性
state 表示线程重入的次数(独占模式)或者剩余许可数(共享模式)
state API:
protected final int getState()
:获取 state 状态protected final void setState(int newState)
:设置 state 状态protected final boolean compareAndSetState(int expect,int update)
:CAS 安全设置 state
2.AQS内部封装线程Node节点中的waitStatus设计
1.使用 volatile 修饰配合 CAS 保证其修改时的原子性
2.表示 Node 节点的状态,有以下几种状态:
// 默认为 0
volatile int waitStatus;
// 由于超时或中断,此节点被取消,不会再改变状态
static final int CANCELLED = 1;
// 此节点后面的节点已(或即将)被阻止(通过park),【当前节点在释放或取消时必须唤醒后面的节点】
static final int SIGNAL = -1;
// 此节点当前在条件队列中
static final int CONDITION = -2;
// 将releaseShared传播到其他节点
static final int PROPAGATE = -3;
3.阻塞恢复设计:
- 使用 park & unpark 来实现线程的暂停和恢复,因为命令的先后顺序不影响结果
- park & unpark 是针对线程的,而不是针对同步器的,因此控制粒度更为精细
- park 线程可以通过 interrupt 打断
3.队列设计
使用了 FIFO 先入先出队列,并不支持优先级队列,同步队列是双向链表,便于出队入队
// 头结点,指向哑元节点
private transient volatile Node head;
// 阻塞队列的尾节点,阻塞队列不包含头结点,从 head.next → tail 认为是阻塞队列
private transient volatile Node tail;
static final class Node {
// 枚举:共享模式
static final Node SHARED = new Node();
// 枚举:独占模式
static final Node EXCLUSIVE = null;
// node 需要构建成 FIFO 队列,prev 指向前继节点
volatile Node prev;
// next 指向后继节点
volatile Node next;
// 当前 node 封装的线程
volatile Thread thread;
// 条件队列是单向链表,只有后继指针,条件队列使用该属性
Node nextWaiter;
}
自定义同步器
AQS的设计是基于模板方法模式,该模式是基于继承的,主要是为了在不改变模板结构的前提下在子类中重新定义模板中的内容以实现复用代码。
- 使用者继承
AbstractQueuedSynchronizer
并重写指定的方法 - 将 AQS 组合在自定义同步组件的实现中,并调用其模板方法,这些模板方法会调用使用者重写的方法
AQS 使用了模板方法模式,自定义同步器时需要重写下面几个 AQS 提供的模板方法:
isHeldExclusively() //该线程是否正在独占资源。只有用到condition才需要去实现它
tryAcquire(int) //独占方式。尝试获取资源,成功则返回true,失败则返回false
tryRelease(int) //独占方式。尝试释放资源,成功则返回true,失败则返回false
tryAcquireShared(int) //共享方式。尝试获取资源。负数表示失败;0表示成功但没有剩余可用资源;正数表示成功且有剩余资源
tryReleaseShared(int) //共享方式。尝试释放资源,成功则返回true,失败则返回false