为什么InterruptedExceptions清除线程的中断状态?

如果一个线程在Object.wait()Thread.join()内部被中断,它会抛出一个InterruptedException它会重置线程的中断状态 。 例如,如果我在Runnable.run()有这样的循环:

 while (!this._workerThread.isInterrupted()) { // do something try { synchronized (this) { this.wait(this._waitPeriod); } } catch (InterruptedException e) { if (!this._isStopping()) { this._handleFault(e); } } } 

调用interrupt()后线程将继续运行。 这意味着我必须通过在循环条件中检查自己的停止标志,重新抛出exception或添加break来明确地中断循环。

现在,这不是一个问题,因为这种行为有很好的记录,并不妨碍我按照我想要的方式做任何事情。 但是,我似乎并不理解它背后的概念:为什么抛出exception后线程不再被视为中断了? 如果使用interrupted()而不是isInterrupted()获得中断状态,也会发生类似的行为,那么,线程也只会出现一次中断。

我在做什么不寻常的事吗? 例如,在循环外捕获InterruptedException更常见吗?

(虽然我不是一个初学者,但我标记了这个“初学者”,因为对我来说这看起来像是一个非常基本的问题,看着它。)

想法是应该处理一次中断。 如果显式的InterruptedException没有清除“interrupt”标志,那么InterruptedException大多数捕获者都必须明确地清除该标志。 相反,您可以通过自我中断( Thread.currentThread().interrupt() )“清除”该标志。 Java的设计人员一直在寻找能够在大多数时间保存击键的语义(即你经常想要清除标志而不是设置它)。

像这样写你的代码,你不需要一个标志:

 try { while (!this._workerThread.isInterrupted()) { // do something synchronized (this) { this.wait(this._waitPeriod); } // do something else } } catch (InterruptedException e) { // ignore ... } 

正如@Boyan指出的那样,一般来说,压制中断exception是个坏主意。 在这种情况下,上下文将确定是否应该压缩它(如上所述),设置中断标志(再次)或允许exception传播。 除其他外,它取决于中断对应用程序的意义。

它不应该。 这是一个令人遗憾的设计缺陷,使得依赖中断成为一个有风险的业务,因为库代码通常会捕获InterruptedException而不重置线程的中断标志并继续。 如果您碰巧在该特定的已损坏的库代码运行时发出线程中断信号,那么当您的代码重新获得执行控制时,它将不会发现中断发生的线索。

这只需要在你从代码中调用的任何地方发生一次,所以为了能够中断一个线程然后使用被中断的位来安全地控制你从所述线程内部的流量,你需要100%确保你所调用的每一段代码都不会错误地清除被中断的位。 当涉及到库时,这很难做到,但即使您可以考虑您在代码中使用的每个库,仍然不能解决可能会犯同样错误的错误JRE代码 。

事实上,它只需要一个库(或JRE!)作者不关心或考虑中断以打破需要它的代码逻辑,这表明这是错误的默认操作。 不关心线程中断位的人可能不会在捕获InterruptedException后重置它 – 也许他们甚至不知道它存在! 如果捕获InterruptedException没有重置线程的中断状态,那么任何不知道被中断位的人都会自动“做正确的事”并且不会因依赖中断的任何调用代码而导致问题。 任何需要清除它的人仍然可以手动执行此操作,但随后它将是一个明确的操作,它比捕获已检查的InterruptedExceptionexception的通常无意识的副作用更可能是正确的。 就像现在一样,如果你依赖线程的中断位,任何调用Thread.sleep()调用堆栈中的任何人都可能会破坏你的一天。

因此,大多数Javamultithreading代码将仅使用“isRunning”实例字段复制Java线程中断模型,并使用某种机制将其作为变通方法进行翻转。

这是因为InterruptedException被认为是一个exception事件,其他人试图从外部阻止一个线程。

当你想真正中断一个线程时,你只需通过设置一个布尔值或类似的东西来打破它的循环条件。 或者从该线程内部使用.wait().notify() 。 但是如果你在外面做wait()

  • 抛出exception以通知外部线程试图打断我或让我等待
  • 线程继续工作,因为它不接受来自另一个线程的任何订单! 但exception的提升允许你添加特殊处理并做任何你想做的事情,也有效地停止线程。