未来的取消不工作

问题描述 投票:9回答:4

我有一个很好的紧凑的代码,但没有像我预期的那样工作。

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

    }

}

输出是:

超时true

结束

问题:为什么调用Runnable的future.cancel(true)方法不终止呢?

java java.util.concurrent
4个回答
10
投票

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

你可以按照下面的方法修改你的代码,应该可以按照预期的方式工作。

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

2
投票

这总是有一点误导性。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");

}

}


1
投票

当您取消一个 FutureRunnable 已经开始, interrupt 方法是在 Thread 正在运行的 Runnable. 但这不一定能让线程停止。 事实上,如果它被卡在一个紧密的圈里,就像你这里的这个, Thread 不会停止。 在这种情况下 interrupt 方法只是设置了一个名为 "中断状态 "的标志,它告诉线程在可以停止的时候停止。

参见 的Javadoc。interrupt 方式 Thread


1
投票

Future.cancel() 将取消任何排队的任务,或者调用 线程.中断() 如果您的线程已经在运行,您需要中断您的代码。

你需要中断你的代码

你的代码的责任是为任何中断做好准备。我想说的是,每当你有一个长期运行的任务时,你都要插入一些像这样的中断准备代码。

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

     ... do something long

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

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

你有几个选择。

  1. 如果你的你是在... Runnable.run() 只需返回或脱离循环,完成该方法。
  2. 你可能在代码深处的其他方法中。这时可能会让该方法抛出 被打断的异常 所以你就会这么做(让标志清空)。
  3. 但是,也许在你的代码深处,抛出 被打断的异常. 在这种情况下,你应该抛出一些其他异常,但在这之前,再次标记你的线程中断,这样捕获的代码就会知道有一个中断正在进行中。下面是一个例子。
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

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

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

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.