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;
/**
* CANCELLED,值为1,表示当前的线程被取消
* SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
* CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
* PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
* 值为0,表示当前节点在sync队列中,等待着获取锁。
*/
volatile int waitStatus;
// 前驱结点
volatile Node prev;
// 后继结点
volatile Node next;
// 与该结点绑定的线程
volatile Thread thread;
// 存储condition队列中的后继节点
Node nextWaiter;
// 是否为共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 获取前驱结点
final Node predecessor() throwsNullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial heador SHARED marker
}
Node(Thread thread, Node mode) { // Used byaddWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { //Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Node类中有两个常量SHARE和EXCLUSIVE,顾名思义这两个常量用于表示这个结点支持共享模式还是独占模式,共享模式指的是允许多个线程获取同一个锁而且可能获取成功,独占模式指的是一个锁如果被一个线程持有,其他线程必须等待。多个线程读取一个文件可以采用共享模式,而当有一个线程在写文件时不会允许另一个线程写这个文件,这就是独占模式的应用场景。
waitStatus状态值解释
类型 | 值 | 说明 |
CANCELLED | 1 | 当前节点由于超时或者中断被取消,节点进入这个状态以后将保持不变。 注:这是唯一大于0的值,很多判断逻辑会用到这个特征 |
SIGNAL | -1 | 后继节点会依据此状态值来判断自己是否应该阻塞、当前节点的线程如果释放了同步状态或者被取消、会通知后继节点、后继节点会获取锁并执行(当一个节点的状态为SIGNAL时就意味着其后置节点应该处于阻塞状态) |
CONDITION | -2 | 当前节点正处在等待队列中,在条件达成前不能获取锁。 |
PROPAGATE | -3 | 当前节点获取到锁的信息需要传递给后继节点,共享锁模式使用该值 |
INITIAL | 0 | 节点初始状态,默认值,表示当前节点在sync同步阻塞队列中,等待着获取锁 |
独占模式下,在同步队列中,节点的状态值为 1,-1,0;在等待队列中节点的状态值为1,-2,0
AQS中有两种队列,一是等待队列,二是同步阻塞队列。
2. 同步队列的结构
这里提一下,同步队列的头节点其实要么是一个无意义的空节点,要么是以 当前正在执行的线程 构建的节点,所以头节点其实是不阻塞的,或者说正是头节点阻塞了后续节点。
共享锁跟独占锁是同一个同步队列,也就是说同步队列中的节点既可以是共享类型也可以是独占类型(暂时不懂)
3. 等待队列的结构