如果我没有调用Java线程的interrupt()方法,那么是谁在调用它?

90

我已经读过并反复阅读了《Java并发实践》,在这个主题上阅读了几个线程,阅读了IBM文章处理InterruptedException,但还有一些事情我简单地不能理解,我认为可以分成两个问题:

  1. 如果我从来没有自己中断其他线程,什么会触发InterruptedException

  2. 如果我从来没有使用interrupt()自己中断其他线程(比如因为我使用其他手段来取消我的工作线程,像毒丸和while (!cancelled)样式循环[如JCIP中所述]),那么InterruptedException是什么意思?在捕获一个时我应该做什么?关闭我的应用程序吗?

9个回答

52

线程中断机制是让一个(协作的)线程响应停止请求的首选方式。任何线程(包括本身)都可以在另一个线程上调用interrupt()

实际上,interrupt()的正常使用情况涉及到某种框架或管理器告诉某些工作线程停止正在进行的操作。如果工作线程“知道中断”,它将通过异常或定期检查其中断标志来注意到已被中断。注意到中断后,良好的线程将放弃正在进行的操作并结束自己。

假设上述使用情况,在Java框架内运行代码或从某个工作线程运行代码时,您的代码可能会被中断。当它被中断时,您的代码应放弃正在进行的操作,并通过最合适的手段终止自身。根据代码的调用方式,这可能是通过返回或抛出某些适当的异常来完成的。但是可能不应该调用System.exit()。(您的应用程序不一定知道为什么被中断,也肯定不知道是否有其他需要由框架中断的线程。)

另一方面,如果您的代码不打算在某些框架的控制下运行,则可以认为InterruptedException是一个意外的异常,即错误。在这种情况下,您应该将该异常视为其他错误一样处理;例如,将其包装在未经检查的异常中,并在处理其他意外的未经检查的异常的同一点上捕获和记录它。(或者,您的应用程序可以简单地忽略中断并继续执行正在执行的操作。)


1)如果我从未自己中断其他线程,什么会触发InterruptedException?

一个例子是,如果使用ExecutorService执行您的Runnable对象,并且在服务上调用shutdownNow()。理论上,任何第三方线程池或线程管理框架都可以合法地执行类似的操作。

2) 如果我从未使用interrupt()打断其他线程,那么InterruptedException是什么意思?我在捕获异常时应该怎么做?关闭我的应用程序吗?

你需要分析代码库以查明是什么在调用interrupt()并为什么这样做。一旦你弄清楚了这一点,就可以确定你的应用程序需要做什么。

在你知道为什么抛出InterruptedException之前,我建议将其视为严重错误;例如,在日志文件中打印堆栈跟踪并关闭应用程序。(显然,这并不总是正确的答案...但是重要的是这是“bug”,需要引起开发者/维护者的注意。)

3) 如何找出是谁/什么调用了interrupt()?

没有好的答案。我最好的建议是在Thread.interrupt()上设置断点并查看调用堆栈。


12

如果您决定将您的代码与其他库集成,它们可以在您的代码上调用interrupt()。例如,如果您决定将来在ExecutorService中执行您的代码,则可能通过interrupt()强制关闭。

简而言之,我会考虑不仅仅是您的代码现在在哪里运行,还要考虑它以后可能运行的环境。例如,您将其放入库中吗?容器中?其他人将如何使用它?您将重新使用它吗?


我以为只有shutdownNow会调用interrupt()方法。对于shutdown也是这样吗? - Harinder

9
正如其他人指出的那样,中断线程(实际上是中断阻塞调用)通常用于清理退出或取消正在进行的活动。
然而,你不应该仅仅将 InterruptedException 视为“退出命令”。相反,你应该把中断看作是控制线程运行状态的一种手段,就像 Object.notify() 一样。在从 Object.wait() 调用中醒来后检查当前状态一样(你不要假设唤醒意味着等待条件已满足),在被中断后你应该检查 为什么 被中断。通常有办法做到这一点。例如,java.util.concurrent.FutureTask 有一个 isCancelled() 方法。
代码示例:
public void run() {
    ....
    try {
        .... // Calls that may block.
    } catch (InterruptedException e) {
        if (!running) {  // Add preferred synchronization here.
            return; // Explicit flag says we should stop running.
        }
        // We were interrupted, but the flag says we're still running.
        // It would be wrong to always exit here. The interrupt 'nudge'
        // could mean something completely different. For example, it
        // could be that the thread was blocking on a read from a particular
        // file, and now we should read from a different file.
        // Interrupt != quit (not necessarily).
    }
    ....
}
public void stop() {
    running = false; // Add preferred synchronization here.
    myThread.interrupt();
}

3

您可以通过创建自己的线程类(扩展 java.lang.Thread)并覆盖interrupt()方法来学习这个知识点,在其中记录堆栈跟踪到一个String字段中,然后转移到super.interrupt()。

public class MyThread extends Thread {

    public volatile String interruptStacktrace; // Temporary field for debugging purpose.

    @Override
    public void interrupt() {
        interruptStacktrace = dumpStack(); // You implement it somehow...

        super.interrupt();
    }
}

3
这个问题出在"I"上。"I"通常指的是类的一个单独实例。我的意思是任何特定的低级代码(类)都不应该依赖于整个系统的实现。话虽如此,你确实需要做一些"架构"决策(比如选择运行平台)。
可能会出现来自JRE的意外中断,这些中断包括java.util.concurrent中被取消的任务以及关闭小程序。
线程中断处理通常写得不正确。因此,我建议在可能的情况下避免引起中断的架构决策。然而,处理中断的代码应该始终编写正确。现在无法将中断从平台中删除。

嗨Tom,我从c.l.j.p.记得你的名字 ;)嗯,确切地说:我从来没有不得不自己抛出interrupt()......除非我捕获InterruptedException并需要重新断言中断状态,但这对我仍然不是100%清楚。我在这里是新手,并对投票和答案/评论的数量感到惊讶(无论是正确的还是错误的):显然,这是一个不平凡的主题,或者至少通常解释不清。话虽如此,由于所有帖子的贡献,我开始更清楚地了解正在发生的事情:) - SyntaxT3rr0r

1
我想我明白你为什么会对中断有点困惑。请参考我的回答:
如果我从来没有自己打断其他线程,那么什么会触发InterruptedException?
首先,您可能会打断其他线程;我知道在JCiP中提到过,您不应该打断您不拥有的线程;然而,这个声明必须得到适当的理解。它的意思是,您在任何任意线程中运行的代码都不应该处理中断,因为它不是线程的所有者,所以它对于其中断策略一无所知。因此,您可以请求打断其他线程,但让其所有者采取打断动作;它将中断策略封装在内部,而不是您的任务代码;至少要礼貌地设置中断标志!
仍然有很多原因可能会导致中断,比如超时、JVM中断等。
如果我从来没有使用interrupt()自己打断其他线程(比如因为我使用其他方法取消我的工作线程,比如毒丸和while (!cancelled)样式循环[如JCIP中所述]),那么InterruptedException意味着什么?我在捕获一个InterruptedException时应该做什么?关闭我的应用程序吗?
你需要非常小心; 如果你拥有抛出InterruptedException(IE)的线程,那么你知道在捕获它时该怎么做,比如关闭应用程序/服务或者用一个新线程替换被杀死的线程!但是,如果你不拥有这个线程,在捕获IE后,要么将其重新抛出到调用堆栈的更高位置,要么在进行某些操作(可能是日志记录)之后,重置中断状态,以便拥有此线程的代码在控制到达它时可以了解到线程已被中断,因此采取适当的行动,因为只有它知道中断策略。
希望这能帮到你。

1

如前所述,另一个库可能会中断您的线程。即使该库没有从您的代码中显式访问线程,它们仍然可以通过以下方法获取正在运行的线程列表并以此方式中断它们。


0

InterruptedException 表示一个程序 可能 会被中断,但不一定会被中断。

如果你没有预料到这个中断,那么你应该像处理其他意外异常一样对待它。如果它出现在一个关键部分,其中意外异常可能会产生严重后果,最好尝试清理资源并优雅地关闭(因为接收到中断信号意味着你的精心设计的应用程序并不依赖于中断的使用方式,所以必须有什么问题)。或者,如果相关代码是非关键或微不足道的,你可能想忽略(或记录)中断并继续执行。


0

目前为止没有人提到:中断可能发生是由于操作系统发送的信号而导致的。例如,kill -SIGINT <pid> 可以向您的进程发送一个中断信号,您的进程随后可以将其转换为线程中断。但默认情况下,JVM已经以特定的方式处理了SIGINT(以及其他如SIGTERM等)。所以,如果您想以特定的方式处理SIGINT,您需要相应地配置JVM和您的应用程序。

通常,Web框架会添加关闭挂钩来允许您优雅地结束工作。通常,您的代码应该尝试优雅地关闭像ExecutorService这样的实例,并设置某些超时时间,然后您应该中断线程。您的线程应该及时响应该中断,以便您的应用程序不会花费太长时间来关闭。但是,仅当您的应用程序需要尝试完成其当前工作时才会有影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接