What Do You Do With InterruptedException?

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

InterruptedException是Java中的一个永久性痛点,尤其对于初级开发者而言。但它不应该是这样的。它是一个相当简单和易于理解的概念。让我试着描述和简化它。

让我们从这段代码开始:

它有什么作用?什么都没有,它只是无休止地旋转CPU。我们能终止它吗?在Java中不行。只有当整个JVM停止运行时,当你按下Ctrl-C时,它才会停止。在Java中没有办法终止一个线程,除非这个线程自行退出。这是我们必须记住的原则,其他一切都会显而易见。

让我们将这个无限循环放入一个线程中:

那么,当我们需要停止一个线程时,我们应该怎么做呢?

以下是在Java中的设计方法。每个线程中都有一个我们可以从外部设置的标志。线程可能会偶尔检查该标志并停止执行。自愿停止!以下是具体方法:

这是唯一一种请求线程停止的方法。在这个例子中有两种方法被使用。当我调用 loop.interrupt() 时,一个标志位会被设置为 true,这个标志位在线程 loop 内的某处。当我调用 interrupted() 方法时,标志位会被返回并立即被设置为 false。是的,这就是这个方法的设计。它检查标志位,返回它,并将其设置为 false。这个设计很丑陋,我知道。

因此,如果我在线程内从未调用过 Thread.interrupted(),并且在标志位为 true 时不退出,那么无人能够停止我。实际上,我会忽略他们对 interrupt() 的调用。他们会请求我停止,但我会忽略他们。他们将无法中断我。

因此,总结我们迄今为止学到的知识,一个正确设计的线程会定期检查标志位并优雅地停止。如果代码不检查标志位并且从未调用 Thread.interrupted(),那么它接受这个事实:迟早会被点击 Ctrl-C 强制终止。

到目前为止,听起来合理吗?希望如此。

现在,JDK 中有一些方法可以为我们检查标志位,并在标志位被设置时抛出 InterruptedException。例如,这就是 Thread.sleep() 方法的设计(采用了一种非常原始的方法)。

为什么要这样做?为什么不能等待并永远不检查标志?嗯,我相信这样做是有原因的。原因如下(如有错误,请纠正):代码要么运行得飞快,要么具备中断准备,没有中间状态。

如果你的代码很快,你就不会检查中断标志,因为你不想处理任何中断。如果你的代码执行较慢,可能需要几秒钟的时间,那就明确声明并以某种方式处理中断。

这就是为什么InterruptedException是一个受检异常的原因。它的设计告诉你,如果你想暂停几毫秒,就要让你的代码具备中断准备。实际上,它的实现如下所示:

好吧,你可以让它浮动到一个更高的级别,那里的人将负责捕捉它。关键是有人必须捕捉它并处理这个线程。理想情况下,只是停止它,因为这就是标志的作用。如果抛出InterruptedException,这意味着有人检查了标志,我们的线程必须尽快完成正在进行的操作。

线程的所有者不希望再等待下去。我们必须尊重我们的所有者的决定。

因此,当你捕捉到InterruptedException时,你必须尽一切可能完成你正在进行的操作并退出。

现在,再看一下Thread.sleep()的代码:

记住,Thread.interrupted()不仅会返回标志位,还会将其设置为false。因此,一旦抛出InterruptedException,标志位会被重置。线程不再知道由所有者发送的中断请求。

线程的所有者要求我们停止,Thread.sleep()检测到该请求,将其移除,并抛出InterruptedException。如果再次调用Thread.sleep(),它将不知道那个中断请求,并且不会抛出任何异常。

明白我的意思了吗?不要轻易忽略InterruptedException,它非常重要。我们不能只是吞下它然后继续执行。那将严重违反整个Java多线程的理念。我们的所有者(即我们线程的所有者)要求我们停止,而我们却无视了。这是一个非常糟糕的主意。

这就是我们大多数人对InterruptedException所做的事情:

这看起来很合乎逻辑,但不能保证高级别实际上会停止一切并退出。它们只能在那里捕获运行时异常,线程仍将保持激活状态。线程的所有者会感到失望。

我们必须通知上一层级,我们刚刚捕获了一个中断请求。我们不能简单地抛出一个运行时异常。这样的行为太不负责任。整个线程接收了一个中断请求,我们只是将其吞下并转换为一个RuntimeException。我们不能如此轻率地对待这样一个严重的情况。

这是我们必须做的:

我们将标志位设置回true

现在,没有人会责怪我们对一个有价值的标志持有不负责任的态度。我们发现它处于true状态,清除了它,将其设置回true,然后抛出了一个运行时异常。接下来会发生什么,我们不在乎。

我想就这样了。您可以在这里找到更详细和官方的问题描述:Java理论与实践:处理InterruptedException

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-15 at 06:45

sixnines availability badge   GitHub stars