在固定时间后中断线程,是否必须抛出InterruptedException异常?

5

我希望在固定的时间后中断一个线程。有人问了同样的问题,而且得到了得票最高的答案(https://dev59.com/IHE95IYBdhLWcg3wheRR#2275596),我稍微简化了一下这个解决方案。

import java.util.Arrays;
import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.invokeAll(Arrays.asList(new Task()), 2, TimeUnit.SECONDS);
        executor.shutdown();
    }
}

class Task implements Callable<String> {
    public String call() throws Exception {
        try {
            System.out.println("Started..");
            Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
            System.out.println("Finished!");
        } catch (InterruptedException e) {
            System.out.println("Terminated!");
        }
        return null;
    }
}

他们补充道:sleep() 不是必需的,只是用于 SSCCE/演示目的。只需在其中执行您的长时间运行任务而非 sleep()。但是,如果您将 Thread.sleep(4000); 替换为 for (int i = 0; i < 5E8; i++) {},则无法编译,因为空循环不会抛出 InterruptedException。而线程要可中断,就需要抛出 InterruptedException。有没有办法让上述代码可以使用一般的长时间运行任务来替代 sleep() 呢?
4个回答

5
如果你希望你的操作可以被中断(即在完成之前可以中断它),那么你需要使用其他可中断操作(Thread.sleep,InputStream.read,请点击此处了解更多信息),或者在循环条件中手动检查线程中断状态,使用Thread.isInterrupted。

1
我认为你是正确的。我希望能够在任何时候中断操作/任务,而不必在操作/任务内部编写特殊代码来检查它是否被中断,但我想这可能是不可能的。 - user1310503

4
你可以检查线程的中断状态,例如:

您可以检查线程的中断状态,例如:

public static void main(String[] args) throws Exception {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.invokeAll(Arrays.asList(new Task()), 2, TimeUnit.SECONDS);
    executor.shutdown();
}

static class Task implements Callable<String> {

    public String call() throws Exception {
        System.out.println("Started..");
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupted!");
                return null;
            }
        }
        System.out.println("Finished!");
        return null;
    }
}

但是我想做的是随时中断一个通用任务,无论它正在做什么。我不想在任务中插入特殊代码来检查它是否被中断,也不希望它只能在每次迭代的一个特定点中断 - 例如,某个特定迭代可能需要很长时间。 - user1310503
3
这不是Java处理中断的方式 - 中断是一种合作过程,其中中断代码建议线程中断,但每个被中断的线程可以根据自己的需求来实现该请求,包括什么也不做并继续正常执行(显然不推荐)。有一个Thread.stop()方法(参见此处),但它已被弃用,不应使用。 - assylias
2
换句话说,在Java中,如果你想让一段代码可被中断,你需要编写它以意识到中断并处理它们。 - assylias

3
你理解有误。
“……线程需要抛出InterruptedException才能中断”这个说法根本不正确。catch块的存在只是因为Thread.sleep()方法会抛出InterruptedException异常。如果你没有使用sleep(或任何其他可能会抛出InterruptedException异常的代码),那么你就不需要该catch块。

如果你这样做,程序就无法工作 - 线程永远不会被中断。我认为我写的是正确的。 - user1310503
知道我所写的是正确的 :-) 我怀疑你不能中断线程的原因是因为你已经让CPU处于忙碌状态,它需要在抛出“InterruptedException”之前进行某种系统调用。 - dty
1
我不明白你所说的“在抛出InterruptedException之前”的意思。我们谈论的是一些不会抛出InterruptedException的代码,对吗?如果你所写的是真的,我会非常感激如果你能提供一些代码,它(a)可以用作Task.call()的主体,(b)不涉及InterruptedException,(c)被Test.main()正确中断? - user1310503
1
它必然涉及InterruptedException。但不必重新抛出它或声明它为抛出的异常之类的事情。例如,如果您在循环内部将一些输出写入文件,而不是仅进行繁忙旋转,则可能会发现您可以更好地中断它。当您中断线程时,直到您进行或从系统调用返回,或者您被阻止在某些系统调用(读取、睡眠等)内部之前,它才会生效。你无法中断纯粹消耗CPU的线程,就像你的繁忙旋转循环正在做的那样。 - dty

-2

如果您替换了sleep,则代码将不会抛出InterruptedException。 您应该删除捕获InterruptedException的try-catch块:

public String call() {
    System.out.println("Started..");
    for (int i = 0; i < 5E8; i++) {}
    System.out.println("Finished!");
    return null;
} 

1
如果你这样做,程序就无法正常工作——线程永远不会停止。 - user1310503

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