繁体   English   中英

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

    }

}

输出是:

超时真

结尾

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

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

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

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

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

所有这些方法(cancel、done、...)都与在 Executor 结构中管理 Threads 相关。 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 ,将在运行RunnableThread上调用interrupt方法。 但这不一定会停止线程。 事实上,如果它被困在一个紧密的循环中,就像你在这里看到的那样, Thread不会停止。 在这种情况下, interrupt方法只是设置一个称为“中断状态”的标志,它告诉线程在可以时停止。

Threadinterrupt方法Javadoc

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();
          }
     }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM