注意: 我们此时并没有断开node的nextWaiter,所以最后一定不要忘记将这个链接断开。
再回到transferAfterCancelledWait调用处,可知,由于transferAfterCancelledWait将返回true,现在checkInterruptWhileWaiting将返回THROW_IE,这表示我们在离开await方法时应当要抛出THROW_IE异常。
interruptMode现在为THROW_IE,则我们将执行break,跳出while循环。接下来我们将执行acquireQueued(node, savedState)进行争锁,注意,这里传入的需要获取锁的重入数量是savedState,即之前释放了多少,这里就需要再次获取多少
情况一总结:
线程因为中断,从挂起的地方被唤醒
随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值为Node.CONDITION,说明并没有signal发生过
然后我们修改线程的waitStatus为0,并通过enq(node)方法将其添加到同步队列中
接下来线程将在同步队列中以阻塞的方式获取,如果获取不到锁,将会被再次挂起
线程在同步队列中获取到锁后,将调用unlinkCancelledWaiters方法将自己从条件队列中移除,该方法还会顺便移除其他取消等待的锁
最后我们通过reportInterruptAfterWait抛出了InterruptedException
因此:
由此可以看出,一个调用了await方法挂起的线程在被中断后不会立即抛出InterruptedException,而是会被添加到同步队列中去争锁,如果争不到,还是会被挂起;
只有争到了锁之后,该线程才得以从同步队列和条件队列中移除,最后抛出InterruptedException。
所以说,一个调用了await方法的线程,即使被中断了,它依旧还是会被阻塞住,直到它获取到锁之后才能返回,并在返回时抛出InterruptedException。中断对它意义更多的是体现在将它从条件队列中移除,加入到同步队列中去争锁,从这个层面上看,中断和signal的效果其实很像,所不同的是,在await()方法返回后,如果是因为中断被唤醒,则await()方法需要抛出InterruptedException异常,表示是它是被非正常唤醒的(正常唤醒是指被signal唤醒)。
情况二中断发生时,线程已经被唤醒过包含以下两种情况
a. 被唤醒时,已经发生了中断,但此时线程已经被signal过了
由于transferAfterCancelledWait返回了false,则checkInterruptWhileWaiting方法将返回REINTERRUPT,这说明我们在退出该方法时只需要再次中断因为signal后条件队列加入到了同步队列中所以node.nextWaiter为空了,所以直接走到了reportInterruptAfterWait(interruptMode)方法
if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); // 这里我们的interruptMode=THROW_IE,说明发生了中断, // 则将调用reportInterruptAfterWait if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } } // 在interruptMode=THROW_IE时,就是简单的抛出了一个InterruptedException private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); // 这里并没有抛出中断异常,而只是将当前线程再中断一次。 else if (interruptMode == REINTERRUPT) selfInterrupt(); }情况二中的第一种情况总结:
线程从挂起的地方被唤醒,此时既发生过中断,又发生过signal
随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值已经不为Node.CONDITION,说明signal发生于中断之前
然后,我们通过自旋的方式,等待signal方法执行完成,确保当前节点已经被成功添加到同步队列中
接下来线程将在同步队列中以阻塞的方式获取锁,如果获取不到,将会被再次挂起
最后我们通过reportInterruptAfterWait将当前线程再次中断,但是不会抛出InterruptedException
b. 被唤醒时,并没有发生中断,但是在抢锁的过程中发生了中断