AQS(抽象队列同步器)
1、AQS解决了什么问题
CAS自旋实现的轻量级锁的两大问题
- CAS空自旋浪费大量CPU资源
- 导致“总线风暴”(大量的cas操作导致总线流量剧增)
关于总线风暴以及总线锁的知识,参考http://t.csdn.cn/3wLCD
解决CAS恶性空自旋有效方式(空间换时间)两种方式
- 分散热点,类似LongerAdder、ConcurrentHashMap
- 队列削峰(JUC通过AQS实现)
2、学习AQS之前,了解一下CLH锁(作为AQS的雏形)
CLH锁的特点:
- 单向FIFO队列(公平锁)
- 竞争资源在一个时间点只能被一个线程锁访问(即队列的头部,表示占用锁的节点)
- 新加入的抢锁线程需要等待,通过CAS插入队列尾部
3、AQS的内部队列
AQS的特点:
- 双向FIFO队列
- 每个Node封装了线程,分别指向前驱节点和后继节点
3.1 Node节点(静态内部类)
static final class Node {
//表示线程是因为获取共享资源时阻塞,而被添加到队列中的
static final Node SHARED = new Node();
// 表示线程因为获取独占资源时阻塞,而被添加到队列中的。
static final Node EXCLUSIVE = null;
//以下是节点的状态
//取消状态 1
static final int CANCELLED = 1;
//节点等待状态 -1:标识后继线程处于等待状态
static final int SIGNAL = -1;
//节点等待状态 -2:标识当前线程处于条件等待
static final int CONDITION = -2;
//节点等待状态 -3:标识下一次共享锁的acquireShared操作需要无条件传播
static final int PROPAGATE = -3;
//节点状态:Canceled Signal Condition Propagate 0
//普通同步节点的初始值为0,条件等待节点的初始值为-2
volatile int waitStatus;
//前驱节点,当前节点会在前驱节点上自选,检查前驱节点的waitStatus的状态
volatile Node prev;
volatile Node next;
volatile Thread thread;
//如果当前Node不是普通节点,而是条件等待节点,则节点处与某个条件的等待队列上
//此属性指向下一个条件等待节点
Node nextWaiter;
waitStatus属性:
- Canceled:标识该线程节点已释放(超时、中断),已取消的节点不会被阻塞,需要从等待队列中取消等待
- Signal:表示其后继节点处于等待状态,如果当前节点被取消或释放,会通知其后继节点
- Condition:
- Propagate:waitStatus为-3时,表示该下个线程获取共享锁,自己的共享状态会被无条件的传播下去,因为共享锁可能出现同时有N个锁可以用
3.2 AQS的核心成员
//队头节点
private transient volatile Node head;
//队尾节点
private transient volatile Node tail;
//标记为状态,AQS适用state标识锁的状态
private volatile int state;
//设置同步状态(无法保证原子性)
protected final void setState(int newState) {
state = newState;
}
//通过同步状态(通过CAS保证原子性)
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
关于ReentrantLock可重入特性
ReentrantLock初始化state为0,表示未锁定状态。线程A调用tryAcquire()独占该锁并使其state+1.其他线程对该锁tryAcquire()会失败。直到A线程unlock该锁的state为0(释放锁)。A线程在此过程中,可以重复获取该锁(state累加),累加多少次,就要释放多少次。
3.3 AbstractOwnableSynchronizer(查看占用该锁(独占锁的情形下生效)的线程)
AQS继承了AbstractOwnableSynchronizer
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/** Use serial ID even though all fields transient. */
private static final long serialVersionUID = 3737899427754241961L;
/**
* Empty constructor for use by subclasses.
*/
protected AbstractOwnableSynchronizer() {
}
/**
* The current owner of exclusive mode synchronization.
独占锁情形下,占用该锁的线程
*/
private tra