1.介绍:
AQS指的是AbstractQueuedSynchronizer(队列同步器),
AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架。
ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等并发类均是基于AQS来实现的,
具体用法是通过继承AQS实现其模板方法,然后将子类作为同步组件的内部类。
例如CountDownLatch,源码发现其内部有一个继承了 AbstractQueuedSynchronizer 的内部类 Sync。
2.谁实现的
AQS是JDK1.5之后才出现的,由大名鼎鼎的Doug Lea李大爷来操刀设计并开发实现,全部源代码(加注释)2315行,整体难度中等。
3.基本架构图解(关键点volatile语义下的共享变量state)
总结:AQS使用一个int类型的成员变量state来表示同步状态,
当state>0时表示已经获取了锁,当state = 0时表示释放了锁。
它提供了三个方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))
来对同步状态state进行操作,当然AQS可以确保对state的操作是安全的。
4.State(共享资源变量,它是int数据类型的)
基本方法:getState(),setState(int newState),compareAndSetState(int expect, int update)
注:上述3种方式均是原子操作,其中compareAndSetState()的实现依赖于Unsafe的compareAndSwapInt()方法。
源码:
private volatile int state;
// 具有内存读可见性语义
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);
}
5.资源的共享方式
第一种:独占式(Exclusive)
只有单个线程能够成功获取资源并执行,如ReentrantLock。
第二种:共享式(Shared)
多个线程可成功获取资源并执行,如Semaphore/CountDownLatch等。
总的来说:
AQS将大部分的同步逻辑均已经实现好,
继承的自定义同步器只需要实现state的获取(acquire)
和释放(release)的逻辑代码就可以,主要包括下面方法:
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。
负数表示失败;
0表示成功,但没有剩余可用资源;
正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
注:AQS需要子类复写的方法均没有声明为abstract,
目的是避免子类需要强制性覆写多个方法,
因为一般自定义同步器要么是独占方法,要么是共享方法,
只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。
当然,AQS也支持子类同时实现独占和共享两种模式,如ReentrantReadWriteLock。
6.CLH队列(FIFO)
AQS是通过内部类Node来实现FIFO队列的,源代码解析如下:
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;
// 表征线程正在等待触发条件(condition)
static final int CONDITION = -2;
// 表征下一个acquireShared应无条件传播
static final int PROPAGATE = -3;
/**
* SIGNAL: 当前节点释放state或者取消后,将通知后续节点竞争state。
* CANCELLED: 线程因timeout和interrupt而放弃竞争state,当前节点将与state彻底拜拜
* CONDITION: 表征当前节点处于条件队列中,它将不能用作同步队列节点,直到其waitStatus被重置为0
* PROPAGATE: 表征下一个acquireShared应无条件传播
* 0: None of the above
*/
volatile int waitStatus;
// 前继节点
volatile Node prev;
// 后继节点
volatile Node next;
// 持有的线程
volatile Thread thread;
// 链接下一个等待条件触发的节点
Node nextWaiter;
// 返回节点是否处于Shared状态下
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回前继节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else return p;
}
// Shared模式下的Node构造函数
Node() { }
// 用于addWaiter
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
// 用于Condition
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
总结:可以看到,waitStatus非负的时候,表征不可用,
正数代表处于等待状态,所以waitStatus只需要检查其正负符号即可,
不用太多关注特定值。
学习来源:https://www.cnblogs.com/wushenghfut/p/11258767.html
https://www.jianshu.com/p/0f876ead2846
AQS(AbstractQueuedSynchronizer)--队列同步器
原文:https://www.cnblogs.com/HuiShouGuoQu/p/13596080.html