Condition条件锁实现(二)
public final void await() throws InterruptedException {
// if (Thread.interrupted()) {
// throw new InterruptedException();
// } else {
// AbstractQueuedSynchronizer.Node node = //this.addConditionWaiter();
// int savedState = //AbstractQueuedSynchronizer.this.fullyRelease(node);
// int interruptMode = 0;
// while(!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) //{
// LockSupport.park(this);
if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
}
线程被挂起后唤醒,有两种情况:
- 其他线程调用了signal方法
- 当前线程被中断了
但是,无论是被中断还是被signal()唤醒,被唤醒的线程最后都将离开condition queue,进入到sync queue中。
随后,线程将在sync queue中利用acquireQueued()方法进行“阻塞式”争锁,抢到锁就返回,抢不到锁就继续被挂起。因此,当await()方法返回时,必然是保证了当前线程已经持有了lock锁。
如果从线程被唤醒,到线程获取到锁这段过程发生过中断,该怎么处理?
中断!中断对于当前线程只是个建议,由当前线程决定怎么对其作出处理。在acquireQueued()方法中,我们对中断是不响应的,只是简单的记录抢锁过程中的中断状态,并在抢到锁后将这个中断状态返回,交于上层调用的函数处理,而这里的“上层调用函数”就是我们的await()方法。
那么await()方法是怎么对待这个中断的,这取决于
中断发生时,线程是否已经被signal过?
如果中断发生时,当前线程并没有被signal过,则说明当前线程还处于条件队列中,属于正常在等待中的状态,此时中断将导致当前线程的正常等待行为被打断,进入到sync queue中抢锁,因此,在await()方法返回后,需要抛出InterruptedException,表示当前线程因为中断而被唤醒。
如果中断发生时,当前线程已经被signal过,这说明这个中断来的太晚了,既然当前线程已经被signal过了,那么就说明在中断发生前,它就已经被正常地从condition queue中唤醒了,所以随后即使发生了中断(这个中断可以发生在抢锁之前,也可以发生在抢锁的过程中),我们都将忽略它,仅仅在await()方法返回后,再自我中断一下,补一下这个中断。
await()方法用中断模式interruptMode这个变量记录中断事件,该变量有三个值:
0:代表整个过程中一直没有中断发生
THROW_IE:表示退出await()方法时需要抛出InterruptedException,这种模式对应于中断发生在signal之前
REINTERRUPT:表示退出await()方法时只需要再自我中断一下,这种模式对应于中断发生在signal之后。
中断先于signal
线程被唤醒后,我们首先将使用checkInterruptWhileWaiting()方法检测中断的模式:
private int checkInterruptWhileWaiting(AbstractQueuedSynchronizer.Node node) {
return Thread.interrupted() ? (AbstractQueuedSynchronizer.this.transferAfterCancelledWait(node) ? -1 : 1) : 0;
}
这里假设已经发生过中断,则Thread.interrupted()方法必然返回true,接下来就是用transferAfterCancelledWait()进一步判断是否发生了signal:
final boolean transferAfterCancelledWait(AbstractQueuedSynchronizer.Node node) {
if (node.compareAndSetWaitStatus(-2, 0)) {
this.enq(node);
return true;
} else {
while(!this.isOnSyncQueue(node)) {
Thread.yield();
}
return false;
}
}
判断一个node是否被signal过,只要判断它是否离开了condition queue,进入到sync queue中。
只要一个节点的waitStatus还是Node.CONDITION,那就说明它还没有signal过。
现在我们分析的是中断先于signal,则当前节点的waitStatus必然是Node.CONDITION,则会成功执行compareAndSetWaitStatus(-2, 0),将该节点的状态设置为0,然后调用enq(node)方法将当前节点添加进sync queue中,然后返回true。
再回到transferAfterCancelledWait调用处,可知,由于transferAfterCancelledWait返回true,现在checkInterruptWhileWaiting将返回THROW_IE,这表示我们在离开await()方法时应当抛出异常。
public final void await() throws InterruptedException {
// if (Thread.interrupted()) {
// throw new InterruptedException();
// } else {
// AbstractQueuedSynchronizer.Node node = //this.addConditionWaiter();
// int savedState = //AbstractQueuedSynchronizer.this.fullyRelease(node);
// int interruptMode = 0;
while(!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
}
interruptMode现在为THROW_IE,则我们将执行break,跳出while循环。
接下来我们将执行acquireQueued(node, savedState)来竞争锁:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这是一个阻塞式的方法,获取到锁则退出,获取不到锁则会被挂起。该方法只有在最终获取到了锁后,才会退出,并且退出时会返回当前线程的中断状态,如果我们在获取锁的过程中又被中断了,则会返回true,否则会返回false。但是这里返回true还是false已经不重要了,因为前面已经发生过中断了,这里就是因为中断而被唤醒的,所以无论如何,我们在退出await()方法时,必然会抛出InterruptedException。
这里假设它获取到了锁,则它将回到调用处,由于这时的interruptMode = THROW_IE,则会跳过if语句执行:
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
这时,当前节点的nextWaiter是有值的,它并没有和原来的condition队列断开,这里已经获取到锁了,在enq(node)方法的setHead()方法中已经将节点的thread属性置为null,从而将当前线程从sync queue移除了,接下来应当将它从condition队列里面移除。由于condition队列是一个单向队列,我们无法获取到它的前驱节点,所以只能从头遍历整个条件队列,然后找到这个节点,再移除它。
然而,事实上,代码并没有这么做。因为既然已经必须从头开始遍历链表了,找到所有waitStatus不为CONDITION的节点,并把它们从队列中移除。
节点被移除后,接下来就是最后一步了——汇报中断状态:
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
这里interruptMode=THROW_IE,说明发生了中断,则将调用reportInterruptAfterWait:
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
可以看出,在interruptMode = THROW_IE时,我们就是简单的抛出了一个InterruptedException。
总结
- 线程因为中断,从挂起的地方被唤醒
- 随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值为Node.CONDITION,说明并没有signal发生过
- 然后我们修改线程的waitStatus为0,并通过enq(node)方法将节点添加到sync queue中
- 加下来线程将在sync queue中以阻塞的方式获取锁,如果获取不到锁,将会被再次挂起
- 线程在sync queue中获取到锁后,将调用unlinkCancelledWaiters方法将自己从条件队列中移除,该方法还会顺便移除其他移除取消等待的节点
- 最后我们通过reportInterruptAfterWait抛出了InterruptedException
由此可以看出,一个调用了await方法挂起的线程在被中断后不会立刻抛出InterruptedException,而是会被添加到sync queue中去争锁,如果争不到,还是会被挂起;只有争到了锁之后,该线程才得以从sync queue中移除,最后抛出InterruptedException。
所以说,一个调用了await方法的线程,即使被中断了,它依旧还是会被阻塞住,直到它获取到锁后才会返回,并在返回时抛出InterruptedException。中断对它的意义更多的是体现在将它从condition queue中移除,加入到sync queue中去争锁,从这个层面上看,中断和signal的效果其实很像,所不同的是,在await方法返回后,如果是因为中断被唤醒,则await方法需要抛出InterruptedException异常,表示它是被非正常唤醒的。
中断后于signal
这样的情况还分为两种:
- 被唤醒时,已经发生了中断,但此时线程已经被signal过了
- 被唤醒时,并没有发生中断,但是在抢锁的过程中发生了中断
被唤醒时,已经发生了中断,但此时线程已经被signal过了
于之前的中断先于signal的主要差别在于transferAfterCancelledWait方法:
final boolean transferAfterCancelledWait(Node node) {
//因为被signal过了,所以waitStatus不再是-2,以下CAS操作失败
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
//等待线程成功进入sync queue
while (!isOnSyncQueue(node))
//从运行态变成就绪态
Thread.yield();
return false;
}
在这里,由于signal已经发生过了,此时当前节点的waitStatus必定不为Node.CONDITION,它将跳过if语句。此时当前线程可能已经在sync queue中,或者正在进入sync queue的路上。
正在进入到sync queue的路上是指,假设当前线程为线程A,它被唤醒之后检测到发生了中断,来到了transferAfterCancelledWait这里,而另一个线程B在这之前已经调用了signal方法,该方法会调用transferForSignal将当前线程添加到sync queue的末尾:
final boolean transferForSignal(AbstractQueuedSynchronizer.Node node) {
if (!node.compareAndSetWaitStatus(-2, 0)) {
return false;
} else {
AbstractQueuedSynchronizer.Node p = this.enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, -1)) {
LockSupport.unpark(node.thread);
}
return true;
}
}
因为线程A和线程B是并发执行的,而这里我们分析的是“中断发生在signal之后”,则此时,线程B的compareAndSetWaitStatus先于线程A执行。这时可能出现线程B已经成功修改了node的waitStatus状态,但是还没来得及调用enq(node)方法,线程A就执行到了transferAfterCancelledWait方法,此时它发现waitStatus已经不是Condition,但是其实当前节点还没有被添加到sync queue队列中,因此,它接下来将通过自旋,等待线程B执行完transferForSignal方法。
线程A在自旋过程中会不断的判断节点有没有被成功添加进sync queue,判断方法就是isOnSyncQueue:
final boolean isOnSyncQueue(AbstractQueuedSynchronizer.Node node) {
if (node.waitStatus != -2 && node.prev != null) {
return node.next != null ? true : this.findNodeFromTail(node);
} else {
return false;
}
}
该方法很好理解,只要waitStatus的值为Node.CONDITION,则它一定还在condition队列中,自然不可能在sync里面;而每一个调用了enq方法入队的线程:
private AbstractQueuedSynchronizer.Node enq(AbstractQueuedSynchronizer.Node node) {
while(true) {
AbstractQueuedSynchronizer.Node oldTail = this.tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (this.compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return oldTail;
}
} else {
this.initializeSyncQueue();
}
}
}
哪怕在设置compareAndSetTail这一步失败了,它的prev必然也是有值的,因此两个条件只要有一个满足,就说明节点必然不在sync queue中。
另一方面,如果node.next有值,则说明它不仅在sync queue中,并且在它后面还有别的节点,则只要它有值,该节点必然在sync queue中。
如果以上都不满足,我们就从尾节点向前寻找这个节点:
private boolean findNodeFromTail(AbstractQueuedSynchronizer.Node node) {
for(AbstractQueuedSynchronizer.Node p = this.tail; p != node; p = p.prev) {
if (p == null) {
return false;
}
}
return true;
}
则这里,由于transferAfterCancelledWait返回false,则checkInterruptWhileWaiting方法返回REINTERRUPT,这说明我们在退出该方法时只需要再次中断。
再回到checkInterruptWhileWaiting方法的调用处:
public final void await() throws InterruptedException {
// if (Thread.interrupted()) {
// throw new InterruptedException();
// } else {
// AbstractQueuedSynchronizer.Node node = //this.addConditionWaiter();
// int savedState = //AbstractQueuedSynchronizer.this.fullyRelease(node);
// int interruptMode = 0;
while(!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
}
此时,interruptMode的值为REINTERRUPT,我们将直接跳出while循环。
接下来就和上面的情况一样了,我们依然还是去争锁,这一步依然是阻塞式的,获取到锁则退出,获取不到锁则会被挂起。
另外由于现在interruptMode的值已经为1,因此无论在争锁的过程中是否发生过中断interruptMode的值都还是REINTERRUPT
接着就是将节点从condition queue中剔除,与情况1不同的是,在signal方法成功将node加入到sync queue时,该节点的nextWaiter已经是null了,所以这里这一步不需要执行。
再接下来就是报告中断状态了:
private void reportInterruptAfterWait(int interruptMode) throws InterruptedException {
if (interruptMode == -1) {
throw new InterruptedException();
} else {
if (interruptMode == 1) {
AbstractQueuedSynchronizer.selfInterrupt();
}
}
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
注意,这里并没有抛出中断异常,而只是将当前线程再中断一次。
总结
- 线程从挂起的地方被唤醒,此时即发生过中断,又发生过signal
- 随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值已经不为Node.CONDITION,说明signal发生于中断之前
- 然后,我们通过自旋的方式,等待signal方法执行完成,确保当前节点已经被成功添加到sync queue中
- 接下来线程将在sync queue中以阻塞的方式获取锁,如果获取不到,将会被再次挂起
- 最后我们通过reportInterruptAfterWait将当前线程再次中断,但是不会抛出InterruptedException
被唤醒时,并没有发生中断,但是在抢锁的过程中发生了中断
这种情况就比上面的情况简单一点了,既然被唤醒时没有发生中断,那基本可以确信线程是被signal唤醒的,但是不要忘记还存在“假唤醒”这种情况,因此我们依然还是要检测被唤醒的原因。
真假唤醒区分
如果线程是被signal而被唤醒,线程最终都会离开condition queue进入sync queue中,所以我们只需要判断被唤醒时,线程是否在sync queue中即可:
public final void await() throws InterruptedException {
// if (Thread.interrupted()) {
// throw new InterruptedException();
// } else {
// AbstractQueuedSynchronizer.Node node = //this.addConditionWaiter();
// int savedState = //AbstractQueuedSynchronizer.this.fullyRelease(node);
// int interruptMode = 0;
while(!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
线程被唤醒时,暂时还没有发生中断,所以这里interruptMode = 0,表示没有中断发生,所以将继续while循环,这是将通过isOnSyncQueue方法判断当前线程是否已经在sync queue中了。由于已经发生过signal了,则此时node必然已经在sync queue中了,所以isOnSyncQueue将返回true,我们将退出循环·。
如果isOnSyncQueue检测到当前节点不在sync queue中,则说明即没有发生中断,也没有发生过signal,则当前线程是被“假唤醒”的,那么我们将再次进入循环体,将线程挂起。
退出while循环后接下来还是利用acquireQueued争锁,因为前面没有发生过中断,则interruptMode = 0,这时,如果在争锁的过程中发生了中断,则acquireQueued将返回true,则此时interruptMode将变为1。
接下是判断node.nextWaiter != null,由于在调用signal方法时已经将节点移出了队列,所以这个条件不成立。
最后就是汇报中断状态了,此时interruptMode的值为REINTERRUPT,说明线程在被signal后又发生了中断,这个中断发生在抢锁的过程中,只需要再次自我中断一下。
总结
- 线程被signal方法唤醒,此时并没有发生过中断
- 因为没有发生过中断,我们将从checkInterruptWhileWaiting处返回,此时interruptMode = 0
- 接下来我们回到while循环中,因为signal方法保证了将节点添加到sync queue中,此时while循环条件不成立,循环退出
- 接下来线程将在sync queue中以阻塞的方式获取,如果获取不到锁,将会被再次挂起
- 线程获取到锁返回后,我们检测到在获取锁的过程中发生过中断,并且此时interruptMode = 0,这时,我们将interruptMode修改为REINTERRUPT
- 最后我们通过reportInterruptAfterWait将当前线程再次中断,但是不会抛出InterruptedException
总结
对于中断发生后于signal之后,将不管这个中断是在抢锁之前就已经发生了还是抢锁的过程中发生了,只是它是在signal之后发生的,将忽略这个中断。因此,从await()方法返回的时候,我们只会将当前线程重新中断一下,而不会抛出中断异常。
一直没有中断发生
这种情况跟上面第二种情况的第二种小情况差不多,只是在抢锁的过程中也没有发生异常,则interruptMode为0,没有发生过中断,因此不需要汇报中断,则线程就从await()方法处正常返回。
await()总结
- 进入await()时必须是已经持有了锁
- 离开await()时同样必须是已经持有了锁
- 调用await()会使得当前线程被封装成Node扔进条件队列,然后释放所持有的锁
- 释放锁后,当前线程将在condition queue中被挂起,等待signal或者中断
- 线程被唤醒后将会离开condition queue进入sync queue中进行抢锁
- 若在线程抢到锁之前发生过中断,则根据中断发生在signal之前还是之后记录中断模式
- 线程在抢到锁后进行善后工作(离开condition queue,处理中断异常)
- 线程已经持有了锁,从await()方法返回
在这一过程中我们尤其要关注中断,如前面所说,中断和signal所起到的作用都是将线程从condition queue中移除,加入到sync queue中去争锁,所不同的是,signal被认为是正常唤醒线程,中断被认为是非正常唤醒线程,如果中断发生在signal之前,则我们最终返回时,应当抛出InterruptedException;如果中断发生在signal之后,我们就认为线程本身已经被正常唤醒了,这个中断来的太晚,我们直接忽略它,并在await()返回时在自我中断一下,这种做法相当于将中断推迟至await()返回时在发生。
awaitUninterruptibly()源码解析
在之前我们分析的await()方法中,中断起到了和signal同样的效果,但是中断属于将一个等待中的线程非正常唤醒,可能即使线程被唤醒后,也抢到了锁,但是却发现当前的等待条件并没有满足,则还是得把线程挂起。因此我们有时候并不希望await方法被中断,
awaitUninterruptibly()方法即实现了这个功能:
public final void awaitUninterruptibly() {
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean interrupted = false;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if (Thread.interrupted())
//发生了中断后线程依旧流程留在了condition queue中,将会再次被挂起
interrupted = true;
}
if (acquireQueued(node, savedState) || interrupted)
selfInterrupt();
}
首先,从方法签名上就可以看出,这个方法不会抛出中断异常,把awaitUninterruptibly()方法和await()方法对比一下:
public final void await() throws InterruptedException {
//不同之处
if (Thread.interrupted())
//不同之处
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
//不同之处
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
//不同之处
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//不同之处
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//不同之处
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//不同之处
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
由此可见,awaitUninterruptibly()全程忽略中断,即使是当前线程因为中断被唤醒,该方法也只是简单的记录中断状态,然后再次被挂起(因为并没有任何操作将它添加到sync queue中)。
要使当前线程离开condition queue去争锁,则必须是发生了signal事件。
最后,当线程在获取锁的过程中发生了中断,该方法也是不响应,只是在最终获取到锁返回时,再自我中断一下。可以看出,该方法和“中断发生于signal之后的”返回值是1的模式的await()方法很像。
总结
- 中断虽然会唤醒线程,但是不会导致线程离开condition queue,如果线程只是因此中断而被唤醒,则他将再次被挂起
- 只有signal方法会使得线程离开condition queue
- 调用该方法时或者调用过程中发生了中断,仅仅会在该方法结束时再自我中断一下,不会抛出InterruptedException。
awaitNanos(long nanosTimeout)源码解析
前面无论是await()还是awaitUninterruptibly(),它们在抢锁过程中都是阻塞式的,即一直到抢到了锁才能返回,否则线程还是被挂起,这样带来一个问题就是线程如果长时间抢不到锁,就会一直被阻塞,因此我们有时候更需要带超时机制的抢锁,这一点和带超时机制的wait(long timeout)是很像的:
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return deadline - System.nanoTime();
}
该方法几乎和await()方法一样,只是多了超时时间的处理。
如果设定的超时时间还没到,我们就将线程挂起;超过等待的时间,我们就将线程从condition queue转移到sync queue中。注意这里对于超时时间有一个小小的优化——当设定的超时时间很短时(小于1000L),我们就是简单的自旋,而不是将线程挂起,以减少挂起线程和唤醒线程所带来的时间消耗。
这里还有一处值得注意,就是awaitNanos(0)的意义,wait(0)的含义是无限期等待,而await(0):
if (nanosTimeout <= 0L) {
AbstractQueuedSynchronizer.this.transferAfterCancelledWait(node);
break;
}
从这里可以看出,如果设置的时间本身就小于等于0,当前线程是会直接从condition queue中转移到sync queue中的,并不会被挂起,也不需要等待signal。如果需要线程只有在signal发生的条件下才会被唤醒,则应该用上面的awaitUninterruptibly()方法更好。
await(long time,TimeUnit unit)源码解析
它就是在上面的awaitNanos(long nanosTimeout)的基础上多了对于超时时间的时间单位的设置,但是在内部实现上还是会把时间转成纳秒去执行:
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
可以看出,这两个方法主要的差别就体现在返回值上面,awaitNanos(long nanosTimeout)的返回值是剩余的超时时间,如果值大于0,说明超时时间还没到,则说明该返回是有signal行为导致的,而await(long time,TimeUnit unit)的返回值是transferAfterCancelledWait(node)的值,我们知道,如果调用该方法时,node还没有被signal过则返回true,如果node已经被signal过了,则返回false。因此await(long time,TimeUnit unit)方法返回true,则说明在超时时间到之前就已经发生过signal了,该方法返回是由signal方法导致的而不是超时时间。
综上,调用await(long time,TimeUnit unit)其实就等价于调用awaitNanos(long nanosTimeout) >0方法。
awaitUntil(Date deadline)源码解析
awaitUntil(Date deadline)方法与上面的几种带超时的方法也基本类似,所不同的是它的超时时间是一个绝对时间:
public final boolean awaitUntil(Date deadline)
throws InterruptedException {
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
可见,这里打断的代码都是重复的,区别就是在超时时间的判断上使用了绝对时间,其实这里的deadline就和awaitNanos(long nanosTimeout)以及await(long time,TimeUnit unit)内部的deadline变量是等价的,另外就是这个方法中,没有使用进行自旋优化,因为一般调用这个方法,目的就是设定一个较长的等待时间,否则使用上面的相对时间会更方便一点。