admin管理员组

文章数量:1579083

2023年12月13日发(作者:)

AbstractQueuedSynchronizer::acquire源码笔记

此处的acquire方法指AQS内部的私有acquire方法,方法签名为

final int acquire(Node node, int arg, boolean shared,

boolean interruptible, boolean timed, long time)

static final int WAITING = 1; // must be 1

static final int CANCELLED = 0x80000000; // must be negative

static final int COND = 2; // in a condition wait

// 此方法为私有的acquire方法,所有暴露出去的acquire方法最终都会使用不同的参数组合调用此方法

final int acquire(Node node, int arg, boolean shared,

boolean interruptible, boolean timed, long time) {

Thread current = tThread();

byte spins = 0, postSpins = 0; // retries upon unpark of first thread

boolean interrupted = false, first = false;

Node pred = null; // predecessor of node when enqueued

/*

* Repeatedly:

* Check if node now first

* if so, ensure head stable, else ensure valid predecessor

* if node is first or not yet enqueued, try acquiring

* else if node not yet created, create it

* else if not yet enqueued, try once to enqueue

* else if woken from park, retry (up to postSpins times)

* else if WAITING status not set, set and retry

* else park and clear WAITING status, and check cancellation

*/

for (;;) {

// 3个判断条件

// 1.此节点当前不是首节点

// 2.此节点前驱节点不为null

// 3.此节点前驱节点不是头结点,注意头结点跟首节点不是一个概念

if (!first && (pred = (node == null) ? null : ) != null &&

!(first = (head == pred))) {

// <0 说明前驱节点已经被取消,此时做一次等待队列清理

if ( < 0) {

cleanQueue(); // predecessor cancelled

continue;

// 如果此节点的前驱节点的前驱节点为null,则直接跳到下一次循环

// Wait(); 可方便虚拟机优化自选等待的过程,本身不包含额外的逻辑

} else if ( == null) {

Wait(); // ensure serialization

continue;

}

}

// 两个并列条件

// 1.当前节点是首节点

// 2.当前节点前驱节点未null,说明当前节点还未入队

// 只有这里的逻辑用到了tryAcquire,说明已经入队的非首节点是不能获取锁的

if (first || pred == null) {

boolean acquired;

// 基于是否为共享获取模式分别调用tryAcquireShared与tryAcquire

// 这两个方法都是无实现方法,需要用户自己实现其语义

try {

if (shared)

acquired = (tryAcquireShared(arg) >= 0);

else

acquired = tryAcquire(arg);

} catch (Throwable ex) {

cancelAcquire(node, interrupted, false);

throw ex;

}

// acquired == true 说明获取锁成功

if (acquired) {

// first == true 说明当前节点为首节点

// 需要将它出队并通知后继节点,这里采用的出队方式是将前导节点删除,将此节点数据清零作为新的头结点

if (first) {

= null;

head = node;

= null;

= null;

// 如果是共享锁,通知后继节点

if (shared)

signalNextIfShared(node);

if (interrupted)

upt();

}

return 1;

}

}

// 走到这里 node == null 说明未获取到锁并且node尚未建立(一个新线程第一次获取锁会出现这种情况)

// 发现这种情况,建立新的node然后重试,新建立的node还没有入队,所以其中的数据是什么无所谓,程序因此选择建立空node

if (node == null) { // allocate; retry before enqueue

if (shared)

node = new SharedNode();

else

node = new ExclusiveNode();

// 走到这里,说明node已经建立但是并未入队,且获取锁失败

// 这种情况下,将node入队然后再次进行尝试

} else if (pred == null) { // try to enqueue

= current;

Node t = tail;

vRelaxed(t); // avoid unnecessary fence if (t == null)

tryInitializeHead();

else if (!casTail(t, node))

vRelaxed(null); // back out

else

= node;

// 当前节点为首节点且自旋倒计时spin!=0

// 此时说明该节点被上一个节点唤醒成为首节点,而且自旋时间还未走完

} else if (first && spins != 0) {

--spins; // reduce unfairness on rewaits

Wait();

// 未获取到锁,且当前node状态为0(未定义)

// 此时node有两种情况,1-第一次入队获取锁失败走到这里; 2-状态为WAITING的情况下获取锁失败被park,然后被前驱节点唤醒,唤醒之后一定为首节点,其状态被复位,然后自旋时间走完来到这里

} else if ( == 0) {

= WAITING; // enable signal and recheck

} else {

long nanos;

// (postSpins << 1) | 1 等价于 postSpins*2+1

spins = postSpins = (byte)((postSpins << 1) | 1);

// 有时间限制的park和无时间限制的park

if (!timed)

(this);

else if ((nanos = time - me()) > 0L)

nos(this, nanos);

else

break;

// 走到这里说明线程从park状态被唤醒

tatus();

if ((interrupted |= upted()) && interruptible)

break;

}

}

return cancelAcquire(node, interrupted, interruptible);

}

本文标签: 节点方法获取前驱