future.cancel不起作用

时间:2015-01-20 10:39:17

标签: java java.util.concurrent

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

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");

    }

}

输出结果为:

  

超时

     

END

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

4 个答案:

答案 0 :(得分:8)

问题在于您的Runnable不可中断:任务中断是Java中的协作过程,取消的代码需要定期检查是否已被取消,否则它将不会响应中断。

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

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

答案 1 :(得分:3)

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

所有这些方法(取消,完成,......)都与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");

}

}

答案 2 :(得分:1)

当您取消Future已经开始的Runnable时,interrupt方法会在Thread上运行Runnable。但这并不一定会阻止这个问题。事实上,如果它被困在一个紧密的循环中,就像你在这里的那个,Thread不会停止。在这种情况下,interrupt方法只设置一个名为&#34;中断状态&#34;的标志,它告诉线程何时停止。

请参阅the Javadoc for the interrupt method of Thread

答案 3 :(得分:1)

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

您需要中断代码

代码的责任是为任何中断做好准备。我想说的很久了,只要您有一个长期运行的任务,就插入一些中断准备好的代码,如下所示:

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

     ... do something long

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

如何停止我在做什么?

您有几种选择:

  1. 如果您位于 Runnable.run()中,只需返回或退出循环并完成该方法即可。
  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();
          }
     }
}

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