一个看似简单的问题:我有一个java.util.concurrent.Semaphore
,我希望使用acquire()
获得许可。
如果线程被中断,则指定acquire()
方法抛出InterruptedException
:
如果是当前帖子:
- 在进入此方法时设置了中断状态;或
- 在等待许可时被打断,
然后抛出InterruptedException并清除当前线程的中断状态。
但是,使用可能抛出InterruptedException
的方法的常用模式是在循环中调用它们,因为线程可能会受到与被中断相同的虚假唤醒。例如,documentation for Object.wait(long)
说:
线程也可以在没有被通知,中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,并且如果条件不满足则继续等待。换句话说,等待应始终在循环中进行。
所以问题是,Semaphore.acquire()
是否受到同样的虚假唤醒?合乎逻辑的答案是“不”,但我找不到任何证据,实际上证据似乎指向另一个方向。
查看source for Semaphore
,它似乎将实际获取权委托给AbstractQueuedSynchronizer
,根据its source委托给LockSupport.park()
。
documentation for LockSupport.park()
明确提到虚假唤醒,但AbstractQueuedSynchronizer.doAcquireInterruptably()
的实施似乎只是检查Thread.interrupted()
然后抛出InterruptedException
。
所以,除非我遗漏某些内容(这是非常可能的),Semaphore.acquire()
可能会虚假地抛出InterruptedException
?
这是对的吗?更重要的是,我能做些什么吗?我可以使用Semaphore.acquireUninterruptably()
,但我不想要一个不间断的等待,只是一个不会被虚假中断的。还有其他选择吗?
答案 0 :(得分:7)
这是"虚假的唤醒"不是"虚假中断":"线程也可以在没有被通知,中断或超时的情况下唤醒,即所谓的虚假唤醒。"在虚假唤醒期间没有抛出InterruptedException。正如你在评论中所说:线程醒来但是没有设置中断的标志。
答案 1 :(得分:2)
我认为如果你考虑Semaphore.acquire()
API,你会发现它不可能有一个虚假的唤醒,主要是因为调用者没有办法区分“假”为“正常”,因此该方法将无用。