ExecutorCompletionService等待最多n秒完成

时间:2015-09-27 05:19:10

标签: java multithreading concurrency executorservice java-threads

我正在使用ExecutorCompletionService来提交一些任务。我想等待最多,比如5秒,然后停止处理。

ExecutorService executorService = Executors.newFixedThreadPool(10);     
CompletionService<String> completionService = new ExecutorCompletionService<String>(
            executorService);
List<Callable<String>> callables = createCallables(); //each callable sleeps randomly between 1-10 seconds and then prints the thread name
for (Callable<String> callable : callables) 
    taskCompletionService.submit(callable);
for (int i = 0; i < callables.size(); i++) {
    Future<String> result = completionService.take();   
    System.out.println(result.get()); 
}

现在我不想等待超过5秒钟来完成所有任务。我只想收集在5秒内完成的任务结果。我怎样才能做到这一点?

executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);

我在shutdown上使用了awaitTerminationexecutorService,但我的主线程仍在等待所有提交的任务完成,所有任务完成并打印需要10秒每个线程的名称。如何在5秒内停止处理?

1 个答案:

答案 0 :(得分:0)

正如您所提到的,主要问题是您的代码正在等待任务完成,然后才能调用shutdown()。基本上这是因为CompletionService.take()将阻塞,直到任务完成。此外,您需要跟踪获得任务结果所需的累计时间,因为CompletionService不会为您做到这一点。

我们的想法是使用poll(long, TimeUnit)并将null结果解释为超时过期,之后您可以立即关闭执行程序服务。这可以这样做:

try {
  ExecutorService executorService = Executors.newFixedThreadPool(10);     
  CompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
  // each callable sleeps randomly between 1-10 seconds and then prints the thread name
  List<Callable<String>> callables = createCallables();
  for (Callable<String> callable : callables) {
    completionService.submit(callable);
  }
  final long timeout = 5_000_000_000L; // 5 seconds in nanos
  long elapsed = 0L;
  int count = 0;
  final long start = System.nanoTime();
  // while not timed out and not all tasks have completed
  while (((elapsed = System.nanoTime() - start) < timeout) && (count < callables.size())) {
    // wait for at most the remaining time before timeout
    Future<String> result = completionService.poll(timeout - elapsed, TimeUnit.NANOSECONDS);
    if (result == null) {
      System.out.println("timed out after " + count + " tasks and " + ((System.nanoTime() - start)/1_000_000L) + " ms");
      break;
    }
    count++;
    System.out.println(result.get()); 
  }
  executorService.shutdownNow();
  System.out.println("done");
} catch (Exception e) {
  e.printStackTrace();
}

我能够测试它是否与createCallables()一起实现,如下所示:

private static List<Callable<String>> createCallables() {
  Random rand = new Random(System.nanoTime());
  List<Callable<String>> list = new ArrayList<>();
  for (int i=0; i<10; i++) {
    // between 1 and 10s
    final long time = 1000L * (1L + rand.nextInt(10));
    list.add(new Callable<String>() {
      @Override
      public String call() throws Exception {
        Thread.sleep(time);
        return "ok after " + time + "s on thread " + Thread.currentThread();
      }
    });
  }
  return list;
}