AQS 是个啥?
AQS,全称 AbstractQueuedSynchronizer,中文直译过来就是 “抽象的队列式的同步器”,它定义了多线程访问共享资源的同步器框架,J.U.C 包中很多同步类实现都依赖于它,比如我们常用的锁:ReentrantLock,又比如一些常用的并发工具类:CountDownLatch、Semaphore 等等。所以,很多人称 AQS 为 J.U.C 包的核心。那么要想吃透 J.U.C 的源码思想,第一步要做的当然就是彻底弄懂 AQS 啊!
AQS 的组成
AQS 整体上来看有两个核心组成部分:
- 一个 int 型的成员变量,它用来表示同步状态。
- 一个 FIFO 双向队列,它用来完成竞争资源的线程的排队工作。
我们接下来具体的来看一下这两个重要的组成部分。
一、同步状态
private volatile int state;
我们可以看到,成员变量 state 是被关键字 volatile 修饰了的,这里主要是为了保证 state 这个变量在多个线程之间的可见性。
那么所谓的加锁/解锁,其实就是对 state 这个同步状态的获取,类似于操作系统中实现进程同步的信号量,多个进程通过检查信号量的值来判断是否可以进入临界区。
同步器的主要使用方式是继承,子类通过继承 AQS 并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态进行更改,这时就需要使用同步器提供的三个方法来操作同步状态:
- getState():获取当前同步状态
- setState(int newState):设置当前同步状态
- compareAndSetState(int expect,int update):使用 CAS 设置当前状态,该方法能够保证状态设置的原子性
源码如下:
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);
}
我们主要来看一下 compareAndSetState(int expect, int update) 这个方法,看名字你就能知道,只要以 ”CompareAndxxxxx “ 开头的,基本上就是使用 unsafe 类的 CAS 操作没跑了,主要是为了保证设置同步状态的操作原子性。
二、同步队列
AQS 通过内置的 FIFO 同步队列来完成资源获取线程的排队工作,如果当前线程获取同步状态失败(锁)时,AQS 则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。
本文旨在先对 AQS 做一个简单的介绍,同步队列的具体内容在该系列的下一篇文章中。
AQS 的使用详解与示例
同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。
那么,接下来我们就来看看,哪些方法是可以重写的,哪些方法是模板方法。
一、可重写方法:
在介绍可重写方法之前,要先让大家明确一点,那就是以下这些方法,不是需要你全部重写(所以这里说的是可重写),这需要根据你想要实现锁的类型(独占锁还是共享锁)来决定你需要重写的方法。
- tryAcquire(int arg):独占式获取同步状态
- tryRelease(int arg):独占式释放同步状态
- tryAcquireShared(int arg):共享式获取同步状态
- tryReleaseShared(int arg):共享式释放同步状态
- isHeldExclusively():当前同步器是否在独占式模式下被线程占用,一般该方法表示是否被当前线程所独占
我们可以看出来,这些开发人员可以重写的方法,几乎都是围绕着同步状态的获取与释放来提供的,我们来看一下 AQS 中的源码。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
thr