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