概述:
队列同步器 AbstractQueuedSynchronizer (简称:同步器、AQS),是用来构建锁或者构建其他同步组件的基础框架,它使用了一个 int 成员变量表示同步状态,通过内置的 FIFO 队列来完成资源获取线程的排队工作。
同步器是一个抽象类,同步器的主要使用方式是继承,子类通过继承同步器并实现其中的抽象方法来管理同步状态。同步状态的改变主要通过三个方法来完成,getState(),setState(int newState),compareAndSetStatus(int expect,int update)。
子类被推荐定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅定义了若干同步状态获取和释放的方法来供自定义同步组件使用。
同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantReadWriteLock 和 CountDownLatch)
同步器与锁的关系
同步器是实现锁的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。
同步器的设计
同步器的设计是基于模板方法模式的,即使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将调用使用者重写的方法。
同步器可重写的方法
同步器提供的模板方法
同步器模板方法基本上分为3类:
独占式获取和释放同步状态、共享式获取和释放同步状态和查询同步队列中的等待线程情况。
队列同步器AQS如何实现同步
主要包括:使用同步队列、独占式同步状态获取和释放、共享式同步状态获取与释放以及超时获取同步状态等同步器的核心数据结构与模板方法。
同步队列
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
同步队列中的节点(Node)用来保存获取同步状态失败的线程引用、等待状态以及前驱后后继节点。
节点的属性类型与名称以及描述如下表所示:
节点是构成同步队列的基础,同步器拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部。同步队列的基本结构如下图所示:
当一个线程成功地获取了同步状态时,其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node expect, Node update),它需要传递当前线程”认为“的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾结点建立连续。
同步队列遵循FIFO(先进先出),首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点
设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置首节点的方法并不需要使用CAS来保证。
节点Node的源码
static final class Node {
static final Node SHARED = new Node(); // 表示当前线程以共享模式持有锁
static final Node EXCLUSIVE = null; // 表示当前线程以独占模式持有锁
static final int CANCELLED = 1; // 表示当前节点已经取消获取锁
static final int SIGNAL = -1; // 表示后继节点的线程需要运行
static final int CONDITION = -2; // 表示当前节点在条件队列中排队
static final int PROPAGATE = -3; // 表示后继节点可以直接获取锁
volatile int waitStatus; // 表示当前节点的等待状态
volatile Node prev; // 同步队列中当前节点的前驱结点
volatile Node next; // 同步队列中当前结点的后继节点
volatile Thread thread; // 当前节点的线程引用
Node nextWaiter; // 表示条件队列中的后继节点
// 判断当前节点是否是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回当前节点的前驱节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 构造器1:无参构造器
Node() {
}
// 构造器2:默认构造器
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
// 构造器3:在条件队列中使用
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
其中SHARED和EXCLUSIVE常量分别代表共享模式和独占模式,所谓共享模式是一个锁允许多条线程同时操作,如信号量Semaphore采用的就是基于AQS的共享模式实现的,而独占模式则是同一个时间段只能有一个线程对共享资源进行操作,多余的请求线程需要排队等待,如ReentranLock。
变量waitStatus则表示当前被封装成Node结点的等待状态,共有4种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE
AQS中的成员变量
同步队列头结点引用,同步队列尾结点引用以及同步状态,
这三个成员变量都使用了 volatile 关键字进行修饰,这就确保了多个线程对它的修改都是内存可见的。整个类的核心就是这个同步状态,可以看到同步状态其实就是一个int型的变量,大家可以把这个同步状态看成一个密码锁,而且还是从房间里面锁起来的密码锁,state 具体的值就相当于密码控制着密码锁的开合