【问题标题】:Performing a long calculation that returns after a timeout执行超时后返回的长时间计算
【发布时间】:2016-02-22 21:10:22
【问题描述】:

我想使用迭代深化来执行搜索,这意味着每次我这样做时,我都会更深入并且需要更长的时间。获得最佳结果有时间限制(2 秒)。根据我的研究,最好的方法是使用 ExecutorService、Future 并在时间用完时中断它。这是我目前拥有的:

在我的主要功能中:

ExecutorService service = Executors.newSingleThreadExecutor();
ab = new AB();
Future<Integer> f = service.submit(ab);
Integer x = 0;
try {
    x = f.get(1990, TimeUnit.MILLISECONDS);
}
catch(TimeoutException e) {
    System.out.println("cancelling future");
    f.cancel(true);
}
catch(Exception e) {
    throw new RuntimeException(e);
}
finally {
    service.shutdown();
}
System.out.println(x);

还有可调用对象:

public class AB implements Callable<Integer> {

    public AB() {}

    public Integer call() throws Exception {
        Integer x = 0;
        int i = 0;
        while (!Thread.interrupted()) {
            x = doLongComputation(i);
            i++;
        }
        return x;
    }
}

我有两个问题:

  1. doLongComputation() 没有被中断,程序仅在完成工作后检查 Thread.interrupted() 是否为真。我是否需要在 doLongComputation() 中检查线程是否已被中断?
  2. 即使我摆脱了 doLongComputation(),主要方法也不会接收 x 的值。如何确保我的程序等待 Callable “清理”并返回迄今为止最好的 x?

【问题讨论】:

    标签: java multithreading concurrency executorservice callable


    【解决方案1】:

    回答第 1 部分:是的,您需要让您的长任务检查中断标志。中断需要被中断任务的配合。

    除非你特别想清除中断标志,否则你应该使用Thread.currentThread().isInterrupted()。抛出(或重新抛出)InterruptedException 的代码使用 Thread#interrupted 作为检查标志和清除标志的便捷方式,当您编写 Runnable 或 Callable 时,这通常不是您想要的。

    现在回答第 2 部分:取消不是您想要的。

    使用取消来停止计算并返回一个中间结果是行不通的,一旦你取消了未来,你就无法从 get 方法中检索返回值。您可以做的是将计算的每个细化都作为自己的任务,这样您就可以提交一个任务,获取结果,然后以结果为起点提交下一个任务,同时保存最新的结果。

    这是我想出的一个例子来证明这一点,使用牛顿法计算平方根的逐次逼近。每次迭代都是一个单独的任务,在前一个任务完成时提交(使用前一个任务的近似值):

    import java.util.concurrent.*;
    import java.math.*;
    
    public class IterativeCalculation {
    
        static class SqrtResult {
            public final BigDecimal value;
            public final Future<SqrtResult> next;
            public SqrtResult(BigDecimal value, Future<SqrtResult> next) {
                this.value = value;
                this.next = next;
            }
        }
    
        static class SqrtIteration implements Callable<SqrtResult> {
            private final BigDecimal x;
            private final BigDecimal guess;
            private final ExecutorService xs;
            public SqrtIteration(BigDecimal x, BigDecimal guess, ExecutorService xs) {
                this.x = x;
                this.guess = guess; 
                this.xs = xs;
            }
    
            public SqrtResult call() {
                BigDecimal nextGuess = guess.subtract(guess.pow(2).subtract(x).divide(new BigDecimal(2).multiply(guess), RoundingMode.HALF_EVEN));
                return new SqrtResult(nextGuess, xs.submit(new SqrtIteration(x, nextGuess, xs)));
            }
        }
    
        public static void main(String[] args) throws Exception {
            long timeLimit = 10000L;
            ExecutorService xs = Executors.newSingleThreadExecutor();
            try {
                long startTime = System.currentTimeMillis();
                Future<SqrtResult> f = xs.submit(new SqrtIteration(new BigDecimal("612.00"), new BigDecimal("10.00"), xs));
                for (int i = 0; System.currentTimeMillis() - startTime < timeLimit; i++) {
                    f = f.get().next;                
                    System.out.println("iteration=" + i + ", value=" + f.get().value);
                }
                f.cancel(true);
            } finally {
                xs.shutdown();
            }
        }
    }
    

    【讨论】:

    • 谢谢!对于第 2 部分,我的问题是 f.cancel(true) 不仅中断了线程,而且完全取消了它。所以 AB 永远不会到达return x
    • @user3389645:我明白你的意思。取消可能不是您想要的。
    猜你喜欢
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-21
    • 2021-06-21
    • 2022-01-13
    相关资源
    最近更新 更多