当前位置: 首页 > 知识库问答 >
问题:

使用ExecutorService时出现超时问题

唐海阳
2023-03-14

我有一个数十万可调用对象的列表。当每一个运行时,它都会根据给定的值执行一个可能很长的计算。正因为如此,我希望异步运行每个任务(最好是通过使用某种执行器),并在30秒后检索每次计算的结果,取消那些没有及时完成的结果。(所得值在其他地方使用。)

到目前为止,我就是这样实现它的:

private void process() {
    class Runner implements Callable<BigDecimal> {
        final int x, y;

        Runner(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public BigDecimal call() {
            BigDecimal gWidth = xMax.subtract(xMin), gHeight = yMax.subtract(yMin);
            BigDecimal gy = gHeight.multiply(BigDecimal.valueOf(-y)).divide(BigDecimal.valueOf(height)).add(yMax);
            BigDecimal gx = gWidth.multiply(BigDecimal.valueOf(x)).divide(BigDecimal.valueOf(width)).add(xMin);

            // The calculation begins when this method is called
            BigDecimal result = calculateAt(gx, gy);
            return result;
        }
    }

    ExecutorService exec = Executors.newCachedThreadPool();
    List<Runner> runners = new ArrayList<>();
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            runners.add(new Runner(x, y));
        }
    }

    try {
        List<Future<BigDecimal>> results = exec.invokeAll(runners, 30, TimeUnit.SECONDS);
        for (Future<BigDecimal> future : results) {
            // Check if the future's task was cancelled and process the results
        }
    } catch (InterruptedException | ExecutionException ex) {
        ex.printStackTrace();
    }
    exec.shutdown();
}

// Extra variables and methods
BigDecimal xMin = BigDecimal.valueOf(-7),
           xMax = BigDecimal.valueOf(7),
           yMin = BigDecimal.valueOf(-7),
           yMax = BigDecimal.valueOf(7);
int height = 850, width = 850;
private BigDecimal calculateAt(BigDecimal x, BigDecimal y) {
    try {
        // Just to simulate a potential execution time
        Thread.sleep((ThreadLocalRandom.current().nextInt(45) + 1) * 1000);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    return BigDecimal.ONE;
}

ArrayListrunners存储每个要执行的callable,然后将其发送到ExecutorService以运行所有任务。我遇到的问题是,任务似乎是同步启动的,而在超时30秒后,只有前40或5万个任务完成了,更不用说开始执行了。

似乎正在发生的是ExecutorService.InvokeAll方法只允许30秒的窗口用于列表中的所有任务开始和完成执行。相反,我需要的是在每个任务的基础上开始这个30秒的窗口,也就是说,一旦任务已经开始,就允许30秒的时间来完成它。InvokeAll似乎不能做到这一点,至少在使用NewCachedThreadPool时不能做到这一点。有Java库或其他实现方法吗?

共有1个答案

傅安宁
2023-03-14

我认为您可以使用completablefuture来解决问题。

例如,下面是一段基于问题片段的代码:

private static final ExecutorService executor = Executors.newCachedThreadPool();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(100);

private static void processAsync() {
  List<CompletableFuture<Object>> futureList = IntStream.range(0, height).boxed()
    .flatMap(y -> IntStream.range(0, width).boxed().map(x -> new Runner(x, y)))
    .map(runner ->
      CompletableFuture.anyOf(
        CompletableFuture.supplyAsync(runner, executor),
        timeout(Duration.ofSeconds(30))
      ).exceptionally(throwable -> {
        // timeout is handled here
        return BigDecimal.ZERO;
      })
    )
    .collect(Collectors.toList());

  CompletableFuture.allOf(futureList.toArray(new CompletableFuture<?>[0]))
    .thenAccept(v -> {
      List<BigDecimal> results = futureList.stream()
        .map(CompletableFuture::join)
        .map(r -> (BigDecimal) r)
        .collect(Collectors.toList());
      // process final results
      BigDecimal sum = results.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
      System.out.println("Final sum: " + sum);
    })
    .exceptionally(throwable -> {
      System.out.println("Failed allOf with " + throwable);
      return null;
    });
}

private static CompletableFuture<BigDecimal> timeout(Duration duration) {
  CompletableFuture<BigDecimal> future = new CompletableFuture<>();
  scheduler.schedule(
    () -> future.completeExceptionally(new TimeoutException("Timeout " + Thread.currentThread().getName())), duration.toMillis(), MILLISECONDS);
  return future;
}

private static class Runner implements Supplier<BigDecimal> {...

这里的主要思想是使用completablefuture.anyof,并将其应用于有用任务的completablefuture和超时任务的COPMPLETablefuture。通过使用ScheduleDexecutorServiceCompletableFuture.CompleteExceptional来实现超时。因此,基本上,anyof返回result或TimeoutException

 类似资料:
  • 问题内容: 快速回顾一下-我有一个Java EE前端,可以接受用户请求,然后针对每个请求使用ExecutorService(SingleThreadedExecutor设置为守护程序)启动冗长的工作流,该工作流包含在库中并且可以工作很好,并且在通过Eclipse以独立模式运行时按预期运行。当从website(servlet)调用时,我观察到工作流始终在初始化Velocity Engine(Velo

  • 我已经使用ExecutorService实现了超时任务。在下面的方法中,我正在提交超时任务,如果它在给定的时间内超时,我将取消该任务并关闭执行器。 它运行得很好,我没有任何问题。 然而,我想知道这是否是最好的代码设计。我只是想知道如果使用ExecutorService返回的未来是否会更好。submit()获取可调用函数的返回值或超时超时任务。例如 我正在使用JDK7。

  • 2.)AVD没有互联网问题(原因稍后解释)。 3.)与我的volley singleton类或请求队列无关(原因稍后解释)。 所以我想我在Volley/Request Future的用法上犯了某种错误。

  • 我在使用SpecifyKind时遇到问题。我发现,如果我将字符串(“O”)应用于DateTime.Now或new DateTime(),则会使spantime不同。我不知道这是怎么可能的。我尝试将这些日期时间保存为数据库中的Utc,但由于跨度不同,时间将不正确。 我就是这样做的: 现在:2021-12-08 13:40:35现在:2021-12-08T13:40:35.1159209 01:00日

  • 问题内容: 我在应用程序中使用Gson,为此,我使用了一些名称与使用Json相同的类。我的应用程序运行良好,但是在编写proguard时,应用程序崩溃了,我猜有些类正在缩小。我的错误是: java.lang.ClassCastException:com.google.gson.internal.StringMap无法转换为com.sample.package.GsonClass 问题答案: 您需要

  • 我使用的是NetBeans 8.1,我有一个SpringBoot项目,它具有以下依赖项: 它们都是在依赖项中正确下载的。 该项目有3个java类和一个扩展JpaRepository的接口 在src/main/资源项下- 在html标签上,我有错误:本地名称为“xmlns: th不可序列化为XML 1.0的属性。如果我试图运行该项目并转到http://localhost:8080/页面,我有一个白标