简体   繁体   English

future.cancel 不起作用

[英]future.cancel does not work

I have a nice and compact code, which does not work as I expected.我有一个漂亮而紧凑的代码,它不像我预期的那样工作。

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

    }

}

The output is :输出是:

Timeout true超时真

END结尾

Question: Why does not terminate the future.cancel(true) method the called Runnable?问题:为什么不终止被调用的 Runnable 的 future.cancel(true) 方法? After the program wrote the "END" to the output, the "r" Runnable is still running.程序将“END”写入输出后,“r”Runnable 仍在运行。

The problem is that your Runnable is not interruptible: task interruption is a collaborative process in Java and the cancelled code needs to check regularly if it's been cancelled otherwise it won't respond to the interruption.问题是你的 Runnable 是不可中断的:任务中断是 Java 中的一个协作过程,被取消的代码需要定期检查它是否被取消,否则它不会响应中断。

You can amend you code as follows and it should work as expected:您可以按如下方式修改代码,它应该可以按预期工作:

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

This is always a little bit misleading: The ExceutorService or even the underlying thread scheduler do not know anything about what the Runnable is doing.这总是有点误导:ExceutorService 甚至底层线程调度程序对 Runnable 正在做什么一无所知。 In your case they don't know that there is a unconditional loop.在您的情况下,他们不知道存在无条件循环。

All these methods (cancel, done, ...) are related to manage Threads in the Executor structure.所有这些方法(cancel、done、...)都与在 Executor 结构中管理 Threads 相关。 cancel cancels the thread from the point of view of the Executor service. cancel从 Executor 服务的角度取消线程。

The programmer must test if the Runnable was canceled and must terminate the run() method.程序员必须测试 Runnable 是否被取消并且必须终止run()方法。

So in your case (if I remember well) something like this:所以在你的情况下(如果我没记错的话)是这样的:

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

}

} }

When you cancel a Future whose Runnable has already begun, the interrupt method is called on the Thread that is running the Runnable .当您取消Runnable已经开始的Future ,将在运行RunnableThread上调用interrupt方法。 But that won't necessarily stop the thread.但这不一定会停止线程。 Indeed, if it's stuck in a tight loop, like the one you've got here, the Thread won't stop.事实上,如果它被困在一个紧密的循环中,就像你在这里看到的那样, Thread不会停止。 In this case, the interrupt method just sets a flag called the "interrupt status", which tells the thread to stop when it can.在这种情况下, interrupt方法只是设置一个称为“中断状态”的标志,它告诉线程在可以时停止。

See the Javadoc for the interrupt method of Thread Threadinterrupt方法Javadoc

Future.cancel() will cancel any queued task or will call Thread.interrupt() on your thread if already running. Future.cancel()将取消任何排队的任务,或者如果您的线程已经运行,则会在您的线程上调用Thread.interrupt()

You need to interrupt your code你需要中断你的代码

It's your code's responsibility is to be ready for any interruptions.您的代码有责任为任何中断做好准备。 I'd go so far to say that whenever you have a long running task, that you insert some interrupt ready code like this:我想说的是,每当你有一个长时间运行的任务时,你就插入一些像这样的中断就绪代码:

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

     ... do something long

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

How to stop what I'm doing?如何停止我正在做的事情?

You have several options:您有多种选择:

  1. If your you are in Runnable.run() just return or break out of the loop and finish the method.如果您在Runnable.run() 中,只需返回或跳出循环并完成该方法。
  2. You may be in some other method deep in the code.您可能在代码深处使用其他方法。 It may make sense at that point for that method to throw InterruptedException so you would just do that (leaving the flag cleared).此时该方法抛出InterruptedException可能是有意义的,因此您只需这样做(清除标志)。
  3. But maybe deep in your code it doesn't make sense to throw InterruptedException .但也许在你的代码深处,抛出InterruptedException是没有意义的。 In that case you should throw some other exception, but before that mark your thread interrupted again so the code that catches knows that an interrupt was in progress.在这种情况下,您应该抛出一些其他异常,但在此之前标记您的线程再次中断,以便捕获的代码知道中断正在进行中。 Here's an example:下面是一个例子:
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

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

Now the exception can propagate an either terminate the thread or be caught, but the receiving code hopefully notices that an interrupt is in progress.现在异常可以传播或终止线程或被捕获,但接收代码有望注意到中断正在进行中。

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

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