java.lang.Thread.interrupt()是做什么用的?

277

当调用java.lang.Thread.interrupt()时,它会将线程的中断状态设置为“中断”。


2
http://www.javaspecialists.co.za/archive/Issue056.html - Joel
4
Java中Thread类的interrupt()方法会打断该线程的等待状态或者阻塞状态并抛出一个InterruptedException异常。如果线程处于非阻塞状态,调用该方法不会立即停止该线程,而是设置线程的中断状态为true。线程可以通过isInterrupted()方法检查自己是否被中断,并在合适的时候做出响应。 - Jacek Laskowski
10个回答

310

Thread.interrupt()方法会设置目标线程的中断状态/标志。然后在该目标线程中运行的代码可以轮询中断状态并适当地处理它。一些阻塞的方法,例如Object.wait()可能会立即消耗中断状态并抛出适当的异常(通常是InterruptedException)。

Java中的中断不是抢占式的。换句话说,为了正确处理中断,两个线程都必须合作。如果目标线程没有轮询中断状态,则中断实际上被忽略。

通过Thread.interrupted()方法进行轮询,该方法返回当前线程的中断状态并清除该中断标志。通常,线程可能会执行某些操作,如抛出InterruptedException。

编辑(来自Thilo评论):一些API方法具有内置的中断处理功能。我能想到的包括:

  • Object.wait()Thread.sleep()Thread.join()
  • 大多数java.util.concurrent结构
  • Java NIO(但不是java.io),它不使用InterruptedException,而是使用ClosedByInterruptException

编辑(来自@thomas-porninexactly same question的回答,以保证完整性)

线程中断是一种温和的方式来促使线程退出。它被用于给线程一个机会以“干净”的方式退出,而不像使用Thread.stop()那样像用突击步枪射击线程。


31
请注意,像sleep或wait这样的方法会自行进行此类轮询并抛出InterruptedException。 - Thilo
1
如果您正在使用可中断的文件I/O,那么效果将不会那么温和。通常会导致文件损坏。 - Marko Topolnik
2
如果你提到了Thread.interrupted,似乎也应该提到还有一个Thread.isInterrupted标志,它不会清除标志,这使得它通常更适合应用程序开发人员使用。 - Nathan Hughes
为什么方法interrupt()即使线程不处于等待或睡眠状态也不能立即中断线程?这种设计有什么目的吗? - Machi

81

什么是中断?

中断是对线程的指示,告诉它应该停止正在进行的工作并做其他事情。由程序员决定线程如何响应中断,但通常线程会终止。

它是如何实现的?

中断机制使用一个称为中断状态的内部标志来实现。调用Thread.interrupt设置此标志。当线程通过调用静态方法Thread.interrupted检查中断时,中断状态将被清除。非静态Thread.isInterrupted用于查询另一个线程的中断状态,不会更改中断状态标志。

Thread.interrupt() API中的引用:

中断此线程。首先调用此线程的checkAccess方法,可能会导致抛出SecurityException异常。
如果此线程被阻塞在Object类的wait()、wait(long)或wait(long, int)方法,或者是在此类的join()、join(long)、join(long, int)、sleep(long)或sleep(long, int)方法中的一个调用中,则其中断状态将被清除,并且它将接收到InterruptedException异常。
如果此线程被阻塞在可中断通道上的I/O操作中,则通道将被关闭,线程的中断状态将被设置,并且线程将接收到ClosedByInterruptException异常。
如果此线程被阻塞在Selector中,则线程的中断状态将被设置,并且它将立即从选择操作中返回,可能带有非零值,就像调用选择器的wakeup方法一样。
如果没有满足前面的条件,则将设置此线程的中断状态。
完全了解相关内容,请参阅此处:

http://download.oracle.com/javase/tutorial/essential/concurrency/interrupt.html


这只是一个部分解释。它也会中断可中断的方法。 - user207421
@EJP,这个问题是关于Thread.interrupt()方法的,它是什么以及如何工作的。我认为我发布的带有链接的答案已经回答了问题。我仍然不明白你想要什么? - YoK
我希望你能更正你部分错误的说法,即它是由标志实现的。那只是故事的一部分。对我来说似乎很清楚。 - user207421
@EJP 谢谢。现在我明白你在说什么了。我已经更新了我的回答,并添加了该方法的 API 文档。这涵盖了我回答中遗漏的可中断方法部分。希望现在我的回答看起来是完整的 :)。 - YoK

23
如果目标线程一直在等待(通过调用wait()或其他相关方法,实质上是做同样的事情,比如sleep()),它将被中断,意味着它停止等待它原本要等待的东西,并收到一个InterruptedException。
在这种情况下,完全由线程自己(调用wait()的代码)决定要做什么。它不会自动终止线程。
有时会与终止标志结合使用。当被中断时,线程可以检查这个标志,然后关闭自己。但再次强调,这只是一种约定。

1
9岁,永远年轻的答案!+1 - Soner from The Ottoman Empire

17

为了完整性,除了其他答案外,如果线程在阻塞在 Object.wait(..)Thread.sleep(..) 上之前被中断,那么这等同于它立即在该方法上阻塞时被中断,如下面的示例所示。

public class InterruptTest {
    public static void main(String[] args) {

        Thread.currentThread().interrupt();

        printInterrupted(1);

        Object o = new Object();
        try {
            synchronized (o) {
                printInterrupted(2);
                System.out.printf("A Time %d\n", System.currentTimeMillis());
                o.wait(100);
                System.out.printf("B Time %d\n", System.currentTimeMillis());
            }
        } catch (InterruptedException ie) {
            System.out.printf("WAS interrupted\n");
        }
        System.out.printf("C Time %d\n", System.currentTimeMillis());

        printInterrupted(3);

        Thread.currentThread().interrupt();

        printInterrupted(4);

        try {
            System.out.printf("D Time %d\n", System.currentTimeMillis());
            Thread.sleep(100);
            System.out.printf("E Time %d\n", System.currentTimeMillis());
        } catch (InterruptedException ie) {
            System.out.printf("WAS interrupted\n");
        }
        System.out.printf("F Time %d\n", System.currentTimeMillis());

        printInterrupted(5);

        try {
            System.out.printf("G Time %d\n", System.currentTimeMillis());
            Thread.sleep(100);
            System.out.printf("H Time %d\n", System.currentTimeMillis());
        } catch (InterruptedException ie) {
            System.out.printf("WAS interrupted\n");
        }
        System.out.printf("I Time %d\n", System.currentTimeMillis());

    }
    static void printInterrupted(int n) {
        System.out.printf("(%d) Am I interrupted? %s\n", n,
                Thread.currentThread().isInterrupted() ? "Yes" : "No");
    }
}

输出:

$ javac InterruptTest.java 

$ java -classpath "." InterruptTest
(1) Am I interrupted? Yes
(2) Am I interrupted? Yes
A Time 1399207408543
WAS interrupted
C Time 1399207408543
(3) Am I interrupted? No
(4) Am I interrupted? Yes
D Time 1399207408544
WAS interrupted
F Time 1399207408544
(5) Am I interrupted? No
G Time 1399207408545
H Time 1399207408668
I Time 1399207408669

含义:如果您像下面这样循环,并且在控制离开 Thread.sleep(..)并绕过循环时发生中断,异常仍将发生。 因此,可以放心地依赖于线程被中断后可靠地抛出InterruptedException:

while (true) {
    try {
        Thread.sleep(10);
    } catch (InterruptedException ie) {
        break;
    }
}

8

线程中断基于标志中断状态。 每个线程的中断状态默认值设置为false。 每当在线程上调用interrupt()方法时,中断状态就被设置为true

  1. 如果中断状态为真(已在线程上调用interrupt()), 那个特定的线程不能进入睡眠状态。如果在该线程上调用sleep,则会抛出中断异常。抛出异常后,标志再次设置为false。
  2. 如果线程已经处于睡眠状态并且调用了interrupt(),则线程将退出睡眠状态并引发中断异常。

4

1

1

Thread.interrupt()方法设置内部的“中断状态”标志。通常,该标志由Thread.interrupted()方法检查。

按照惯例,通过InterruptedException存在的任何方法都必须清除中断状态标志。


1
你有支持这个“规约”的链接吗? - kap
2
是的,我有 - https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html - Anatolii Shuba

0

我想在以上答案中添加一两个内容。

  1. 需要记住的一件事是,调用中断方法并不总是会导致 InterruptedException。因此,实现代码应定期检查中断状态并采取适当的措施。

  2. Thread.currentThread().isInterrupted() 也可以用于检查线程的中断状态。与 Thread.interrupted() 方法不同,它不会清除中断状态。


0

public void interrupt()

中断此线程。

除非当前线程正在中断自己(这始终被允许),否则将调用此线程的checkAccess方法,这可能导致抛出SecurityException异常。

如果此线程在Object类的wait()、wait(long)或wait(long, int)方法,或者在该类的join()、join(long)、join(long, int)、sleep(long)或sleep(long, int)方法的调用中被阻塞,则它的中断状态将被清除,并且它将收到InterruptedException异常。

如果此线程在可中断通道上的I/O操作中被阻塞,则该通道将关闭,线程的中断状态将被设置,该线程将收到ClosedByInterruptException异常。

如果此线程在Selector中被阻塞,则线程的中断状态将被设置,并且它将立即从选择操作中返回,可能带有一个非零值,就像调用选择器的wake-up方法一样。

如果没有任何先前的情况成立,则将设置此线程的中断状态。

中断不活动的线程不必有任何效果。

抛出: SecurityException - 如果当前线程无法修改此线程


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