future.cancel 无效

future.cancel does not work

我有一个漂亮而紧凑的代码,但它没有像我预期的那样工作。

public class Test {

    public static void main(String[] args) {

        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    for (;;) {

                    }
                } finally {
                    System.out.println("FINALLY");
                }

            }
        };

        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future<?> future = executor.submit(r);
        try {
            future.get(3, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            boolean c = future.cancel(true);
            System.out.println("Timeout " + c);
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("interrupted");
        }
        System.out.println("END");

    }

}

输出为:

Timeout true

END

问题: 为什么不终止调用的Runnable 的future.cancel(true) 方法? 程序将 "END" 写入输出后,"r" Runnable 仍然是 运行.

这总是有点误导:ExceutorService 甚至底层线程调度程序对 Runnable 正在做什么一无所知。在您的情况下,他们不知道存在无条件循环。

所有这些方法(cancel,done,...)都与管理Executor结构中的线程有关。 cancel 从 Executor 服务的角度取消线程。

程序员必须测试 Runnable 是否被取消并且必须终止 run() 方法。

所以在你的情况下(如果我没记错的话)是这样的:

public class Test {

public static void main(String[] args) {

    FutureTask r = new FutureTask () {
        @Override
        public void run() {
            try {
                for (;!isCancelled();) {

                }
            } finally {
                System.out.println("FINALLY");
            }

        }
    };

    ExecutorService executor = Executors.newSingleThreadExecutor();

    Future<?> future = executor.submit(r);
    try {
        future.get(3, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
        boolean c = future.cancel(true);
        System.out.println("Timeout " + c);
    } catch (InterruptedException | ExecutionException e) {
        System.out.println("interrupted");
    }
    System.out.println("END");

}

}

当您取消 Runnable 已经开始的 Future 时,interrupt 方法在 运行 Thread 上调用 Runnable。但这不一定会停止线程。事实上,如果它陷入一个紧密的循环中,就像你在这里得到的那样,Thread 将不会停止。在这种情况下,interrupt 方法只是设置一个名为 "interrupt status" 的标志,它告诉线程在可以停止时停止。

参见 the Javadoc for the interrupt method of Thread

问题是你的Runnable是不可中断的:任务中断是Java中的一个协作过程,被取消的代码需要定期检查是否被取消,否则不会响应中断。

您可以按如下方式修改代码,它应该会按预期工作:

Runnable r = new Runnable() {
    @Override public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {}
        } finally {
            System.out.println("FINALLY");
        }
    }
};

Future.cancel() 将取消任何排队的任务或将在您的线程上调用 Thread.interrupt()如果已经 运行.

您需要中断您的代码

您的代码有责任为任何中断做好准备。我想说的是,只要你有一个很长的 运行 任务,你就可以像这样插入一些中断就绪代码:

while (... something long...) {

     ... do something long

     if (Thread.interrupted()) {
         ... stop doing what I'm doing...
     }
}

如何停止我正在做的事情?

您有多种选择:

  1. 如果您在 Runnable.run() 中,只需 return 或跳出循环并完成方法。
  2. 您可能在代码深处的其他方法中。那时该方法抛出 InterruptedException 可能是有意义的,因此您只需这样做(清除标志)。
  3. 但也许在您的代码深处抛出 InterruptedException 没有意义。在那种情况下,您应该抛出一些其他异常,但在此之前标记您的线程再次中断,以便捕获的代码知道中断正在进行中。这是一个例子:
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

          if (Thread.interrupted()) {
             // oh no! an interrupt!
             Thread.currentThread().interrupt();
             throw new SomeOtherException();
          }
     }
}

现在异常可以传播并终止线程或被捕获,但接收代码有望注意到正在进行中断。