参考:
Java
并发编程的艺术
JDK
版本:AdoptOpenJDK 11.0.2+9
1 AQS 队列同步器
队列同步器(AbstractQueuedSynchronizer
,AQS
)是用来构架锁或者其他同步组件的基础框架。它是面向锁的实现者的,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待和唤醒等底层操作,大大降低了实现一个可靠的锁或者同步组件的门槛。
2 AQS 提供的接口
AQS
是基于 模板方法模式 设计的。使用者需要继承同步器并重写指定的方法来操作同步状态,使用的时候要调用同步器提供的模板方法。
下面对同步器提供的方法一一说明。
2.1 访问或修改同步状态的方法
AQS
提供了3
个方法来访问或修改同步状态:
方法 | 说明 |
---|---|
getState() |
获取当前同步状态。 |
setState(int newState) |
设置当前同步状态。 |
compareAndSetState(int expect, int update) |
使用CAS 设置当前状态,该方法能够保证状态设置的原子性。 |
2.2 可重写的方法
AQS
提供了一些可以被重写的方法,这些方法主要是来获取、释放同步状态的。
这些方法都是protected
类型的,可以被子类重写。
方法 | 说明 |
---|---|
protected boolean tryAcquire(int arg) |
独占式获取同步状态。实现该方法需要查询当前同步状态并判断同步状态是否符合预期,然后再进行CAS 设置同步状态。 |
protected boolean tryRelease(int arg) |
独占式释放同步状态。释放后,等待获取同步状态的线程将有机会获取同步状态。 |
protected int tryAcquireShared(int arg) |
共享式获取同步状态。返回大于等于0 的值,表示获取成功;反之,获取失败。 |
protected boolean tryReleaseShared(int arg) |
共享式释放同步状态。 |
protected boolean isHeldExclusively() |
当前同步器是否在独占模式下被线程占用。一般该方法表示是否被当前线程所独占。 |
2.3 模板方法
AQS
提供了一些模板方法,当实现自定义的同步组件的时候,将会调用这些模板方法,而这些模板方法将会去调用被重写的那些方法。
模板方法基本分为3
类:独占式获取与释放同步状态、共享式获取与释放同步状态、查询同步队列中的等待线程。
这些模板方法都是public final
类型的,不可以被重写。
方法 | 说明 |
---|---|
public final void acquire(int arg) |
独占式获取同步状态。如果当前线程获取同步状态成功,则该方法返回;否则,将会进入同步队列等待。该方法将会调用重写的tryAcquire(int age) 方法。 |
public final void acquireInterruptibly(int arg) |
独占式获取同步状态。该方法能够响应中断,当前线程如果没有获取到同步状态,则进入同步队列中等待,如果当前线程被中断,则会抛出InterruptedException 异常。 |
public final boolean tryAcquireNanos(int arg, long nanosTimeout) |
在acquireInterruptibly(int arg) 基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将会返回false ,如果获取到返回true ,如果被中断,抛出InterruptedException 异常。 |
public final void acquireShared(int arg) |
共享式获取同步状态。如果当前线程没有获取到同步状态,将会进入同步队列等待。与独占式的区别是在同一时刻可以有多个线程获取到同步状态。 |
public final void acquireSharedInterruptibly(int arg) |
与acquireShared(int arg) 相同,但是该方法能够响应中断。 |
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) |
在acquireSharedInterruptibly(int arg) 的基础上增加了超时限制。 |
public final boolean release(int arg) |
独占式的释放同步状态。该方法将会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒。 |
public final boolean releaseShared(int arg) |
共享式的释放同步状态。 |
public final Collection<Thread> getQueuedThreads() |
获取等待在同步队列上的线程集合。 |
3 AQS 实现原理
3.1 Node节点
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 compareAndSetWaitStatus(int expect, int update) {
return WAITSTATUS.compareAndSet(this, expect