AQS原理简述
AQS衍生了三个问题:
1.同步状态的操作
2.阻塞/唤醒线程
3.线程等待队列
同步状态
//同步状态
private volatile int state;
protected final int getState() {
return 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);
}
AQS中包含了一个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;//Condition专用 表示节点在Condition队列中,因为某个条件而被阻塞
static final int PROPAGATE = -3;//传播 适用于共享模式
volatile int waitStatus;//后续节点的状态
volatile Node prev;//前驱
volatile Node next;//后驱
volatile Thread thread;//当前节点的线程
Node nextWaiter;//Condition队列使用
final boolean isShared() {//判断当前节点是否为共享模式
return nextWaiter == SHARED;
}
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;
}
}
将节点插入队列的方法如下:
private Node enq(final Node node) {
for (;;) {//自旋操作
//第一次获取尾部节点 可能出现两种情况 当前队列为空 所以进if 创建一个空的node节点 作为尾部节点和头部节点
//第二种情况和第二次在进入一样 在获取尾部节点 这时候进入else 把当前传入的节点的前驱设置当前的尾部节点 在通过CAS操作
Node t = tail;//获取队列尾部节点
if (t == null) { // 当尾部节点为空的时候 说明当前是第一个节点
if (compareAndSetHead(new Node()))//通过CAS操作添加一个头部节点
tail = head;//尾部节点复制给头部
} else {
node.prev = t;//当前尾部节点为传入节点的前驱
if (compareAndSetTail(t, node)) {//直到CAS成功为止
t.next = node;//添加当前节点为后驱节点
return t;//返回当前尾部节点
}
}
}
}