AQS内部体系架构 ( state变量+CLH双端Node队列 )
AQS的两个重要构成
- AQS的int变量 state
- AQS的CLH队列
一、AQS的同步状态State成员变量
查看底层源码
/**
* 同步状态
*/
private volatile int state;
state成员变量,用来记录锁的占有情况
- 当state = 0 ,表示当前锁并没有被线程占有,处于空闲状态
- 当state = 1 ,表示锁被某个线程占有,其他想要占有锁的线程 就应该进入到 CHL 等待队列中等待
二、AQS的CLH队列
CLH的设计 是一个双向队列,查看底层源码
一、头结点
/**
* 等待队列的头,延迟初始化。 除初始化外,只能通过setHead方法进行修改。
* 注意:如果head存在,则保证其waitStatus不被取消
*/
private transient volatile Node head;
二、尾结点
/**
* 等待队列的尾部,延迟初始化。
* 仅通过方法enq进行修改以添加新的等待节点。
*/
private transient volatile Node tail;
三、CLH中封装线程的Node结点
static final class Node {
//共享
static final Node SHARED = new Node();
//独占
static final Node EXCLUSIVE = null;
// waitStatus值,线程被取消
static final int CANCELLED = 1;
// waitStatus值,线程阻塞,等待唤醒
static final int SIGNAL = -1;
/** waitStatus值,等待condition的唤醒
static final int CONDITION = -2;
/**
* waitStatus值,共享状态下获取将会无条件的传播下去
*/
static final int PROPAGATE = -3;
//控制结点的状态
volatile int waitStatus;
//前一个结点
volatile Node prev;
//下一个结点
volatile Node next;
//结点中封装的线程变量
volatile Thread thread;
/**
* 链接到等待条件的下一个节点,或者链接到特殊值SHARED。
* 由于条件队列仅在以独占模式保存时才被访问,因此我们只需要一个简单的链接队列即可在节点等待条件时保存节点。
* 然后将它们转移到队列中以重新获取。
* 由于条件只能是互斥的,因此我们使用特殊值来表示共享模式来保存字段。
*/
Node nextWaiter;
/**
* 如果节点在共享模式下等待,则返回true
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 返回上一个节点,如果为null,则抛出NullPointerException。
* 当前任不能为null时使用。 空检查可能会被忽略,但是可以帮助VM
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() {
// Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) {
// Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) {
// Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Node结点的属性说明
AQS同步队列的基本结构
大致如下图
AQS底层,使用了LockSupport类 来实现线程的等待唤醒,如果不太理解LockSupport 可以看我另外一篇博客
为什么要出现LockSupport?