我必须在 FutureTask 中手动处理中断吗?

问题描述

我目前正在尝试了解 FutureTask.cancel(true) 是如何工作的,这是官方文档的相关部分

如果任务已经开始,则 mayInterruptIfRunning 参数决定是否应该中断执行该任务的线程以尝试停止任务。

这是取自 Github 的 cancel 的实现

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this,stateOffset,NEW,mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this,INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

所以,基本上我们可以看到 cancel(true) 所做的唯一一件事就是在工作线程上调用 interrupt。那么,如果我的 callFutureTask 方法看起来像这样

SomeType call() {
  for(int i = 0; i < 1000000000; i++) {
    //pass
  }
  return someValue;
}

那么,我的问题是 - 我是否必须添加手动检查线程中断才能取消此类类型的 FutureTasks ?我的意思是这似乎很明显,因为我没有调用任何可以处理中断的 IO 函数,也没有检查 Thread.currentThread().isInterrupted(),所以这个任务似乎是不可取消的,但是在我们拥有的任何官方文档中仍然没有提到这一点处理中断或自己可以取消任务,所以最好问问别人的意见。

解决方法

是的,你必须处理CancellationException(这个异常会抛出FutureTask.get()方法),如果你不做,这个异常会被抛给JVM。看这个例子,会输出“任务被取消”:

public class MainTest {
    public static void main(String... args) {
        FutureTask<Long> futureTask = new FutureTask<>(new MyCallable());

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(futureTask);

        try {
            futureTask.cancel(true);
            System.out.println(futureTask.get());
        } catch (CancellationException e) {
            System.out.println("Task is canceled");
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("Something went wrong!");
        }

        executorService.shutdown();
    }
}

class MyCallable implements Callable<Long> {
    @Override
    public Long call() {
        for(int i = 0; i < 1000000000; i++) {
            //pass
        }

        return 1L;
    }
}
,

Java 中没有先发制人。对于任何可中断的事物,它都必须合作。所以是的,任务必须检查它是否被中断,否则即使未来被取消,它也会运行到最后。令人失望,我知道。