判断是否需要阻塞线程shouldParkAfterFailedAcquire
在线程获取同步状态时如果获取失败,则加入同步队列,通过通过自旋的方式不断获取同步状态,但是在自旋的过程中则需要判断当前线程是否需要阻塞,其主要方法在acquireQueued():
if (shouldParkAfterFailedAcquire(p, node) &&
//如果shouldParkAfterFailedAcquire返回true则调用parkAndCheckInterrupt阻塞线程
parkAndCheckInterrupt())
interrupted = true;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//前驱节点的状态
int ws = pred.waitStatus;
//状态为SIGNAL表示当前线程处于等待状态,直接返回true
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
//前驱节点状态>0,则为Canclled,表明该节点已经超时或者被中断,需要从同步队列删除该前驱节点,直到前驱节点状态小于0
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//前驱节点状态为Condition、PROPAGATE
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//通过CAS的方式将前驱节点的状态设置为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
这段代码主要检查当前线程是否需要被阻塞,具体规则如下:
- 如果当前线程的前驱节点状态为SINNAL,则表明当前线程需要被阻塞,调用unpark()方法唤醒,直接返回true,当前线程阻塞
- 如果当前线程的前驱节点状态为CANCELLED(ws > 0),则表明该线程的前驱节点已经等待超时或者被中断了,则需要从CLH队列中将该前驱节点删除掉,直到回溯到前驱节点状态 <= 0 ,返回false
- 如果前驱节点非SINNAL,非CANCELLED,则通过CAS的方式将其前驱节点设置为SINNAL,返回false
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}