今天笔者带大家深入刨析AQS源码的每个方法!!!
方法一:
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
方法解读:如果当前状态值等于预期值,则自动将同步状态设置为给定的更新值。此操作具有 volatile 读写的内存语义。
参数值:参数expect是期望值,参数update是期望值。
返回值:如果成功,返回true。返回false表示实际值不等于预期值。
方法二:
private Node enq(final Node node) {
for (;;) {
Node t = tail;//新建一个节点,使其等于尾部节点
if (t == null) { // 当当前双向链表未进行初始化,先进行初始化
if (compareAndSetHead(new Node()))//底层用C++写的,CAS进行设置该节点
tail = head;//让tail字段重新赋值为 head 字段,等待队列的头部
} else {//已经初始化过的,进行插入队列链表操作
node.prev = t;//节点的前驱设置为当前队列的尾部节点
if (compareAndSetTail(t, node)) {//底层用C++实现,将该节点插入队列中的 t节点后面
t.next = node;//前驱节点指向当前节点
return t;
}
}
}
}
方法解读:将节点插入队列,必要时进行初始化。
参数值:参数node 表示要插入的节点
返回值:返回节点的前驱
方法三:
private Node addWaiter(Node mode) {
//Thread.currentThread()返回对当前正在执行的线程对象的引用。
//然后根据当前线程和传入的模式创建对应的节点
Node node = new Node(Thread.currentThread(), mode);
// 试试enq的快速路径;失败时备份到完整的enq
Node pred = tail;//新建一个节点,使其等于尾部节点
if (pred != null) {//尾节点不为空时,尾插入方式建立队列
node.prev = pred;
if (compareAndSetTail(pred, node)) {//CAS操作队列尾部
pred.next = node;
return node;
}
}
enq(node);
return node;
}
方法解读:为当前线程和给定模式创建和排队节点。
参数值:参数 mode 传入Node.EXCLUSIVE 为独占,Node.SHARED 为共享 返回值:返回新节点
ps: pre:链接到当前节点线程依赖于检查 waitStatus 的前驱节点。在入队期间分配,并且仅在出队时清空(为了 GC)。此外,在取消前任后,我们会在找到未取消的前任时进行短路,这将始终存在,因为头节点永远不会被取消:节点仅在成功获取后才成为头。被取消的线程永远不会成功获取,线程只会取消自己,不会取消任何其他节点。
方法四:
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
方法解读:将队列头设置为节点,从而出队。仅由获取方法调用。为了 GC 和抑制不必要的信号和遍历,还清空未使用的字段。
参数值:参数node 表示节点
方法五:
private void unparkSuccessor(Node node) {
//如果失败或等待线程更改状态,则可以。
int ws = node.waitStatus;//获取状态
if (ws < 0)//如果状态为负(即可能需要信号),请尝试清除以等待信号。
compareAndSetWaitStatus(node, ws, 0);
//要取消停放的线程在后继节点中,通常只是下一个节点。
//但如果取消或明显为空
Node s = node.next;//获取当前节点的后继节点
if (s == null || s.waitStatus > 0) {//节点为空或等待状态值大于 0
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)//则从尾部向后遍历以找到实际未取消的继任者(阻塞状态)。
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//使给定线程的许可证可用(如果它尚不可用)。如果线程在 {@code park} 上被阻塞,那么它将解除阻塞。
//否则,它对 {@code park} 的下一次调用保证不会阻塞。如果给定线程尚未启动,则不保证此操作有任何效果。
LockSupport.unpark(s.thread);
}
方法解读:唤醒节点的后继者(如果存在)。
参数值:参数 node 表示节点