Java 中的 InterruptedException 处理

发布于 2024-09-28 06:00:50 字数 317 浏览 5 评论 0原文

以下处理 InterruptedException 的方法有什么区别?最好的方法是什么?

try {
 // ...
} catch (InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

...或者:

try {
 //...
} catch (InterruptedException e) {
   throw new RuntimeException(e);
}

我还想知道这两者在哪些场景中使用。

What is the difference between the following ways of handling InterruptedException? What is the best way to do it?

try {
 // ...
} catch (InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

...or:

try {
 //...
} catch (InterruptedException e) {
   throw new RuntimeException(e);
}

I'd like to also know in which scenarios are these two used.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

被你宠の有点坏 2024-10-05 06:00:50

以下处理InterruptedException的方式有什么区别?最好的方法是什么?

您可能会问这个问题,因为您调用了一个抛出 InterruptedException 的方法。

首先,您应该了解抛出 InterruptedException 的含义:方法签名的一部分以及调用您正在调用的方法的可能结果。因此,首先要接受一个事实:InterruptedException 是方法调用的完全有效的结果。

现在,如果您调用的方法抛出此类异常,您的方法应该做什么?您可以通过考虑以下问题来找出答案:

您正在实现的方法抛出 InterruptedException 是否有意义? ,调用你的方法时出现InterruptedException是一个合理的结果吗?

  • 如果,那么抛出InterruptedException应该是你的方法签名的一部分,并且你应该让异常传播(即不要在全部)。

示例:您的方法等待来自网络的值来完成计算并返回结果。如果阻塞网络调用抛出 InterruptedException,您的方法将无法以正常方式完成计算。您让 InterruptedException 传播。

intcomputeSum(Server 服务器) 抛出 InterruptedException {
    // 传播下面抛出的任何 InterruptedException
    int a = server.getValueA();
    int b = server.getValueB();
    返回a+b;
}
  • 如果,那么您不应该使用throws InterruptedException声明您的方法,并且您应该(必须!)捕获异常。现在,在这种情况下,请记住两件事:
  1. 有人打断了您的线程。有人可能渴望取消操作,优雅地终止程序,或者其他什么。您应该对那个人有礼貌,然后立即从您的方法中返回。

  2. 尽管您的方法可以在出现InterruptedException的情况下设法产生合理的返回值,但线程已被中断的事实可能仍然很重要。特别是,调用您的方法的代码可能对您的方法执行期间是否发生中断感兴趣。因此,您应该通过设置中断标志来记录发生中断的事实:Thread.currentThread().interrupt()

示例:用户要求打印两个值的总和。如果无法计算总和,则打印“无法计算总和”是可以接受的(并且比让程序因 InterruptedException 而因堆栈跟踪而崩溃要好得多)。换句话说,使用 throws InterruptedException 声明此方法没有意义。

void printSum(服务器服务器) {
     尝试 {
         int sum =computeSum(服务器);
         System.out.println("总和:" + sum);
     } catch (InterruptedException e) {
         Thread.currentThread().interrupt(); // 设置中断标志
         System.out.println("计算总和失败");
     }
}

现在应该很清楚,仅仅抛出新的 RuntimeException(e) 是一个坏主意。这对打电话的人来说不太礼貌。您可以发明一个新的运行时异常,但根本原因(有人希望线程停止执行)可能会丢失。

其他示例:

实现 Runnable:正如您可能已经发现的,Runnable.run 的签名不允许重新抛出 InterruptedExceptions.嗯,签署了实现Runnable的协议,这意味着签署了处理可能的InterruptedExceptions的协议。选择不同的接口,例如 Callable,或者按照上面的第二种方法。


 

调用Thread.sleep:您正在尝试读取文件,规范规定您应该尝试 10 次,中间间隔 1 秒。您调用Thread.sleep(1000)。因此,您需要处理InterruptedException。对于诸如 tryToReadFile 之类的方法,“如果我被中断,我将无法完成尝试读取文件的操作”,这是完全有道理的。。换句话说,该方法抛出 InterruptedExceptions 是完全有意义的。

String tryToReadFile(File f) 抛出 InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            返回 readFile(f);
        线程睡眠(1000);
    }
    返回空值;
}

What is the difference between the following ways of handling InterruptedException? What is the best way to do it?

You've probably come to ask this question because you've called a method that throws InterruptedException.

First of all, you should see throws InterruptedException for what it is: A part of the method signature and a possible outcome of calling the method you're calling. So start by embracing the fact that an InterruptedException is a perfectly valid result of the method call.

Now, if the method you're calling throws such exception, what should your method do? You can figure out the answer by thinking about the following:

Does it make sense for the method you are implementing to throw an InterruptedException? Put differently, is an InterruptedException a sensible outcome when calling your method?

  • If yes, then throws InterruptedException should be part of your method signature, and you should let the exception propagate (i.e. don't catch it at all).

Example: Your method waits for a value from the network to finish the computation and return a result. If the blocking network call throws an InterruptedException your method can not finish computation in a normal way. You let the InterruptedException propagate.

int computeSum(Server server) throws InterruptedException {
    // Any InterruptedException thrown below is propagated
    int a = server.getValueA();
    int b = server.getValueB();
    return a + b;
}
  • If no, then you should not declare your method with throws InterruptedException and you should (must!) catch the exception. Now two things are important to keep in mind in this situation:
  1. Someone interrupted your thread. That someone is probably eager to cancel the operation, terminate the program gracefully, or whatever. You should be polite to that someone and return from your method without further ado.

  2. Even though your method can manage to produce a sensible return value in case of an InterruptedException the fact that the thread has been interrupted may still be of importance. In particular, the code that calls your method may be interested in whether an interruption occurred during execution of your method. You should therefore log the fact an interruption took place by setting the interrupted flag: Thread.currentThread().interrupt()

Example: The user has asked to print a sum of two values. Printing "Failed to compute sum" is acceptable if the sum can't be computed (and much better than letting the program crash with a stack trace due to an InterruptedException). In other words, it does not make sense to declare this method with throws InterruptedException.

void printSum(Server server) {
     try {
         int sum = computeSum(server);
         System.out.println("Sum: " + sum);
     } catch (InterruptedException e) {
         Thread.currentThread().interrupt();  // set interrupt flag
         System.out.println("Failed to compute sum");
     }
}

By now it should be clear that just doing throw new RuntimeException(e) is a bad idea. It isn't very polite to the caller. You could invent a new runtime exception but the root cause (someone wants the thread to stop execution) might get lost.

Other examples:

Implementing Runnable: As you may have discovered, the signature of Runnable.run does not allow for rethrowing InterruptedExceptions. Well, you signed up on implementing Runnable, which means that you signed up to deal with possible InterruptedExceptions. Either choose a different interface, such as Callable, or follow the second approach above.

 

Calling Thread.sleep: You're attempting to read a file and the spec says you should try 10 times with 1 second in between. You call Thread.sleep(1000). So, you need to deal with InterruptedException. For a method such as tryToReadFile it makes perfect sense to say, "If I'm interrupted, I can't complete my action of trying to read the file". In other words, it makes perfect sense for the method to throw InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}
北方的巷 2024-10-05 06:00:50

碰巧我今天早上在上班的路上读到了这篇文章 Java 并发实践< /a> 作者:Brian Goetz 等人。基本上他说你应该做三件事之一:

  1. 传播InterruptedException - 声明你的方法抛出已检查的InterruptedException,以便你的调用者必须处理它。

  2. 恢复中断 - 有时您无法抛出InterruptedException。在这些情况下,您应该捕获 InterruptedException 并通过调用 currentThread 上的 interrupt() 方法来恢复中断状态,以便更高层的代码调用堆栈可以看到发出了中断,并快速从方法返回。 注意:这仅适用于您的方法具有“尝试”或“尽力而为”语义的情况,即,如果该方法未实现其目标,则不会发生任何重要情况。例如,log()sendMetric()可能是这样的方法,或者boolean tryTransferMoney(),但不是voidtransferMoney( )。请参阅此处了解更多详细信息。

  3. 忽略方法内的中断,但在退出时恢复状态 - 例如通过 Guava 的 不间断Uninterruptibles 接管样板代码,如 JCIP § 7.1.3 中的不可取消任务示例。

As it happens I was just reading about this this morning on my way to work in Java Concurrency In Practice by Brian Goetz et al. Basically he says you should do one of three things:

  1. Propagate the InterruptedException - Declare your method to throw the checked InterruptedException so that your caller has to deal with it.

  2. Restore the Interrupt - Sometimes you cannot throw InterruptedException. In these cases you should catch the InterruptedException and restore the interrupt status by calling the interrupt() method on the currentThread so the code higher up the call stack can see that an interrupt was issued, and quickly return from the method. Note: this is only applicable when your method has "try" or "best effort" semantics, i. e. nothing critical would happen if the method doesn't accomplish its goal. For example, log() or sendMetric() may be such method, or boolean tryTransferMoney(), but not void transferMoney(). See here for more details.

  3. Ignore the interruption within method, but restore the status upon exit - e. g. via Guava's Uninterruptibles. Uninterruptibles take over the boilerplate code like in the Noncancelable Task example in JCIP § 7.1.3.

迷鸟归林 2024-10-05 06:00:50

你想做什么?

当一个线程正在等待或睡眠并且另一个线程使用 Thread 类中的 interrupt 方法中断该线程时,会引发 InterruptedException。所以如果捕获到这个异常,就说明线程已经被中断了。通常,再次调用 Thread.currentThread().interrupt(); 是没有意义的,除非您想从其他地方检查线程的“中断”状态。

关于抛出 RuntimeException 的其他选项,这似乎不是一件非常明智的事情(谁会捕获这个?将如何处理它?),但如果没有其他信息,很难告诉更多信息。

What are you trying to do?

The InterruptedException is thrown when a thread is waiting or sleeping and another thread interrupts it using the interrupt method in class Thread. So if you catch this exception, it means that the thread has been interrupted. Usually there is no point in calling Thread.currentThread().interrupt(); again, unless you want to check the "interrupted" status of the thread from somewhere else.

Regarding your other option of throwing a RuntimeException, it does not seem a very wise thing to do (who will catch this? how will it be handled?) but it is difficult to tell more without additional information.

偏爱自由 2024-10-05 06:00:50

正确的默认选择是将 InterruptedException 添加到抛出列表中。中断表示另一个线程希望您的线程结束。此请求的原因并不明显,并且完全是上下文相关的,因此,如果您没有任何其他知识,您应该假设这只是友好关闭,而任何避免关闭的行为都是非友好响应。

Java不会随机抛出InterruptedException,所有建议都不会影响您的应用程序,但我遇到过开发人员遵循“吞下”策略变得非常不方便的情况。一个团队开发了大量测试并大量使用 Thread.Sleep。现在我们开始在 CI 服务器中运行测试,有时由于代码缺陷会陷入永久等待。更糟糕的是,当尝试取消 CI 作业时,它从未关闭,因为旨在中止测试的 Thread.Interrupt 并未中止该作业。我们必须登录到盒子并手动终止进程。

长话短说,如果您只是抛出 InterruptedException,那么您就匹配了线程应该结束的默认意图。如果您无法将 InterruptedException 添加到抛出列表中,我会将其包装在 RuntimeException 中。

有一个非常合理的论点,即 InterruptedException 本身应该是 RuntimeException,因为这将鼓励更好的“默认”处理。它不仅仅是 RuntimeException,因为设计者坚持一条明确的规则,即 RuntimeException 应该代表代码中的错误。由于 InterruptedException 不是直接由代码中的错误引起的,因此事实并非如此。但实际情况是,经常会出现 InterruptedException,因为代码中存在错误(即无限循环、死锁),而 Interrupt 是其他线程处理该错误的方法。

如果您知道需要进行合理的清理,那就去做吧。如果您知道中断的更深层原因,则可以采取更全面的处理。

因此,总而言之,您的处理选择应遵循以下列表:

  1. 默认情况下,添加到抛出。
  2. 如果不允许添加到 throws,则抛出 RuntimeException(e)。 (多个坏选项中的最佳选择)
  3. 仅当您知道中断的明确原因时,才根据需要进行处理。如果您的处理是本地方法,则通过调用 Thread.currentThread().interrupt() 重置中断。

The correct default choice is add InterruptedException to your throws list. An Interrupt indicates that another thread wishes your thread to end. The reason for this request is not made evident and is entirely contextual, so if you don't have any additional knowledge you should assume it's just a friendly shutdown, and anything that avoids that shutdown is a non-friendly response.

Java will not randomly throw InterruptedException's, all advice will not affect your application but I have run into a case where developer's following the "swallow" strategy became very inconvenient. A team had developed a large set of tests and used Thread.Sleep a lot. Now we started to run the tests in our CI server, and sometimes due to defects in the code would get stuck into permanent waits. To make the situation worse, when attempting to cancel the CI job it never closed because the Thread.Interrupt that was intended to abort the test did not abort the job. We had to login to the box and manually kill the processes.

So long story short, if you simply throw the InterruptedException you are matching the default intent that your thread should end. If you can't add InterruptedException to your throw list, I'd wrap it in a RuntimeException.

There is a very rational argument to be made that InterruptedException should be a RuntimeException itself, since that would encourage a better "default" handling. It's not a RuntimeException only because the designers stuck to a categorical rule that a RuntimeException should represent an error in your code. Since an InterruptedException does not arise directly from an error in your code, it's not. But the reality is that often an InterruptedException arises because there is an error in your code, (i.e. endless loop, dead-lock), and the Interrupt is some other thread's method for dealing with that error.

If you know there is rational cleanup to be done, then do it. If you know a deeper cause for the Interrupt, you can take on more comprehensive handling.

So in summary your choices for handling should follow this list:

  1. By default, add to throws.
  2. If not allowed to add to throws, throw RuntimeException(e). (Best choice of multiple bad options)
  3. Only when you know an explicit cause of the Interrupt, handle as desired. If your handling is local to your method, then reset interrupted by a call to Thread.currentThread().interrupt().
公布 2024-10-05 06:00:50

对我来说,关键是:InterruptedException 并不是出现任何问题,而是线程在执行您告诉它执行的操作。因此,重新抛出包含在 RuntimeException 中的它是零意义的。

在许多情况下,当您说“我不知道这里出了什么问题并且我无能为力来修复它,我只是希望它脱离当前处理流程”时,重新抛出包含在 RuntimeException 中的异常是有意义的并点击我拥有的任何应用程序范围的异常处理程序,以便它可以记录它。 InterruptedException 的情况并非如此,它只是线程响应调用了 Interrupted() 的情况,它抛出 InterruptedException 是为了帮助及时取消线程的处理。

因此,传播 InterruptedException,或者智能地吃掉它(意味着在它将完成其本来要做的事情的地方)并重置中断标志。请注意,当抛出 InterruptedException 时,中断标志会被清除; Jdk 库开发人员所做的假设是捕获异常相当于处理它,因此默认情况下该标志被清除。

所以肯定第一种方法更好,问题中的第二个发布的示例没有用,除非您不希望线程实际被中断,并且中断它相当于错误。

这是我写的一个答案 描述中断如何工作,举个例子。您可以在示例代码中看到它使用 InterruptedException 来摆脱 Runnable 的 run 方法中的 while 循环。

To me the key thing about this is: an InterruptedException is not anything going wrong, it is the thread doing what you told it to do. Therefore rethrowing it wrapped in a RuntimeException makes zero sense.

In many cases it makes sense to rethrow an exception wrapped in a RuntimeException when you say, I don't know what went wrong here and I can't do anything to fix it, I just want it to get out of the current processing flow and hit whatever application-wide exception handler I have so it can log it. That's not the case with an InterruptedException, it's just the thread responding to having interrupt() called on it, it's throwing the InterruptedException in order to help cancel the thread's processing in a timely way.

So propagate the InterruptedException, or eat it intelligently (meaning at a place where it will have accomplished what it was meant to do) and reset the interrupt flag. Note that the interrupt flag gets cleared when the InterruptedException gets thrown; the assumption the Jdk library developers make is that catching the exception amounts to handling it, so by default the flag is cleared.

So definitely the first way is better, the second posted example in the question is not useful unless you don't expect the thread to actually get interrupted, and interrupting it amounts to an error.

Here's an answer I wrote describing how interrupts work, with an example. You can see in the example code where it is using the InterruptedException to bail out of a while loop in the Runnable's run method.

佞臣 2024-10-05 06:00:50

我只是想在大多数人和文章提到的内容中添加最后一个选项。正如 mR_fr0g 所说,通过以下方式正确处理中断非常重要:

  • 传播 InterruptException

  • 恢复线程上的中断状态

或者另外:

  • 自定义处理中断

根据您的情况以自定义方式处理中断并没有什么问题。由于中断是终止请求,而不是强制命令,因此完成额外的工作以允许应用程序优雅地处理请求是完全有效的。例如,如果一个线程正在睡眠,等待 IO 或硬件响应,当它收到中断时,那么在终止线程之前优雅地关闭任何连接是完全有效的。

我强烈建议您了解该主题,但本文是一个很好的信息来源:http ://www.ibm.com/developerworks/java/library/j-jtp05236/

I just wanted to add one last option to what most people and articles mention. As mR_fr0g has stated, it's important to handle the interrupt correctly either by:

  • Propagating the InterruptException

  • Restore Interrupt state on Thread

Or additionally:

  • Custom handling of Interrupt

There is nothing wrong with handling the interrupt in a custom way depending on your circumstances. As an interrupt is a request for termination, as opposed to a forceful command, it is perfectly valid to complete additional work to allow the application to handle the request gracefully. For example, if a Thread is Sleeping, waiting on IO or a hardware response, when it receives the Interrupt, then it is perfectly valid to gracefully close any connections before terminating the thread.

I highly recommend understanding the topic, but this article is a good source of information: http://www.ibm.com/developerworks/java/library/j-jtp05236/

川水往事 2024-10-05 06:00:50

我想说,在某些情况下,什么都不做也没关系。默认情况下可能不是您应该做的事情,但是如果没有办法发生中断,我不确定还能做什么(可能记录错误,但这不会影响程序流程)。

一种情况是您有一个任务(阻塞)队列。如果您有一个守护进程线程处理这些任务并且您自己不中断该线程(据我所知,jvm 不会在 jvm 关闭时中断守护线程),我认为中断不可能发生,因此可能是只是忽略了。 (我确实知道守护线程可能随时被 jvm 杀死,因此在某些情况下不适合)。

编辑:
另一种情况可能是受保护的块,至少基于 Oracle 的教程:
http://docs.oracle.com/javase/tutorial/essential/concurrency /guardmeth.html

I would say in some cases it's ok to do nothing. Probably not something you should be doing by default, but in case there should be no way for the interrupt to happen, I'm not sure what else to do (probably logging error, but that does not affect program flow).

One case would be in case you have a task (blocking) queue. In case you have a daemon Thread handling these tasks and you do not interrupt the Thread by yourself (to my knowledge the jvm does not interrupt daemon threads on jvm shutdown), I see no way for the interrupt to happen, and therefore it could be just ignored. (I do know that a daemon thread may be killed by the jvm at any time and therefore are unsuitable in some cases).

EDIT:
Another case might be guarded blocks, at least based on Oracle's tutorial at:
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文