线程中断停止线程

每个 Java 线程都有一个中断标志,最初为 false。中断线程,基本上只是将该标志设置为 true。在该线程上运行的代码可以偶尔检查该标志并对其进行操作。代码也可以完全忽略它。但为什么每个 Thread 都有这样的标志呢?毕竟,在线程上有一个布尔标志是我们可以自己组织的东西,如果需要的话。好吧,有些方法在它们运行的​​线程被中断时以特殊方式运行。这些方法称为阻塞方法。这些是将线程置于 WAITING 或 TIMED_WAITING 状态的方法。当一个线程处于这种状态时,中断它会导致在被中断的线程上抛出 InterruptedException,而不是将中断标志设置为 true,并且线程再次变为 RUNNABLE。调用阻塞方法的代码被强制处理 InterruptedException,因为它是一个经过检查的异常。因此,因此它的名称,中断可以具有中断 WAIT 的效果,从而有效地结束它。请注意,并非所有以某种方式等待的方法(例如阻塞 IO)都以这种方式响应中断,因为它们不会使线程处于等待状态。最后,设置了中断标志的线程进入阻塞方法(即尝试进入等待状态),将立即抛出 InterruptedException,并清除中断标志。请注意,并非所有以某种方式等待的方法(例如阻塞 IO)都以这种方式响应中断,因为它们不会使线程处于等待状态。最后,设置了中断标志的线程进入阻塞方法(即尝试进入等待状态),将立即抛出 InterruptedException,并清除中断标志。请注意,并非所有以某种方式等待的方法(例如阻塞 IO)都以这种方式响应中断,因为它们不会使线程处于等待状态。最后,设置了中断标志的线程进入阻塞方法(即尝试进入等待状态),将立即抛出 InterruptedException,并清除中断标志。

除了这些机制之外,Java 并没有为中断分配任何特殊的语义含义。代码可以任意方式自由解释中断。但大多数情况下,中断通常用于向线程发出信号,它应该尽早停止运行。但是,正如上面应该清楚的那样,该线程上的代码可以适当地对该中断作出反应以便停止运行。停止线程是一种协作。当一个线程被中断时,它的运行代码可以在堆栈跟踪的几个深层。大多数代码都不会调用阻塞方法,并且足够及时完成,不会过度延迟线程的停止。应该主要关注响应中断的代码是循环处理任务的代码,直到没有剩下的代码为止,或直到设置一个标志,表示它停止该循环。处理可能无限任务的循环(即它们原则上保持运行)应检查中断标志以退出循环。对于有限循环,语义可能要求所有任务必须在结束之前完成,或者保留一些未处理的任务可能是适当的。调用阻塞方法的代码将被强制处理 InterruptedException。如果在语义上完全可能,它可以简单地传播 InterruptedException 并声明抛出它。因此,就其呼叫者而言,它本身就成为一种阻止方法。如果它不能传播异常,它应该至少设置中断标志,因此堆栈上方的调用者也知道线程被中断。在某些情况下,无论 InterruptedException 如何,该方法都需要继续等待,

例子 :

在中断时停止处理任务的代码示例

class TaskHandler implements Runnable {
    
    private final BlockingQueue<Task> queue;

    TaskHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) { // check for interrupt flag, exit loop when interrupted
            try {
                Task task = queue.take(); // blocking call, responsive to interruption
                handle(task);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // cannot throw InterruptedException (due to Runnable interface restriction) so indicating interruption by setting the flag
            }
        }
    }
    
    private void handle(Task task) {
        // actual handling
    }
}

延迟设置中断标志直到完成的代码示例:

class MustFinishHandler implements Runnable {

    private final BlockingQueue<Task> queue;

    MustFinishHandler(BlockingQueue<Task> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        boolean shouldInterrupt = false;
        
        while (true) {
            try {
                Task task = queue.take();
                if (task.isEndOfTasks()) {
                    if (shouldInterrupt) {
                        Thread.currentThread().interrupt();
                    }
                    return;
                }
                handle(task);
            } catch (InterruptedException e) {
                shouldInterrupt = true; // must finish, remember to set interrupt flag when we're done
            }
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}

具有固定任务列表但可能在中断时提前退出的代码示例

class GetAsFarAsPossible implements Runnable {

    private final List<Task> tasks = new ArrayList<>();

    @Override
    public void run() {
        for (Task task : tasks) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }
            handle(task);
        }
    }

    private void handle(Task task) {
        // actual handling
    }
}