并发 - foreach循环中的非阻塞设计

时间:2012-11-21 19:29:16

标签: java multithreading concurrency future

我正在处理一个返回List<Response>对象的服务。

有两种对象可以构建响应。其中一个从内部数据填充内容,另一个到达服务并获取数据。

for(Item item : items){
   if(item is external){
     call service and get result and populate Response and add to List
   }

   if(item is internal)
   {
     populate response object and add to list
   }
}

当前的impl是程序性的和阻塞的,我想要的是一种非阻塞设计,这样,如果该项目是外部激活该呼叫并继续列表中的下一项。然后当循环完成时,我可以等待所有完成。

这样做的好方法是什么?我还考虑按责任创建单独的课程。

编辑:引入的原因是减少延迟命中率。

3 个答案:

答案 0 :(得分:5)

ExecutorCompletionService(带回调的线程池记住提交的任务)看起来像个傻瓜:

CompletionService<Response> executorService = 
    new ExecutorCompletionService<>(Executors.newFixedThreadPool(10));
int totalExternal = 0;

for(Item item : items){
    if(item is external){
        executorService.submit(externalCall(item));
        ++totalExternal;
    }

    if(item is internal){
        populate response object and add to list
    }
}

for (int i = 0; i < totalExternal; ++totalExternal) {
    addAsynchResultToResponseList(executorService.take().get());
}

为清晰起见,externalCall(item)的定义如下:

Callable<Response> externalCall(Item item) {
  return new Callable<Response>() {
    //...
  }
}

显然,一旦你进入异步,结果列表可以有任意顺序。


另一种方法是使用普通ExecutorService并使用中间List<Future<Response>>。诀窍是使用AsyncResult包装器来包装内部响应(它创建立即完成的Future并返回传递的值)。

List<Future<Response>> futures = new ArrayList<>();
for(Item item : items){
    if(item is external){
        futures.add(executorService.submit(externalCall(item)));
    }

    if(item is internal){
        futures.add(new AsyncResult(synchResponse));
    }
}

现在您可以简单地遍历futuresAsyncResult将立即返回,因为该值在创建时已经是计算机(synchResponse)。但是你必须等待从线程池返回的Future

请记住,Future.get()允许您检索原始异常。此外,列表中Future的顺序与原始项的顺序相同,因此如果第Future个失败,items列表中的第n个项目就是原因。

答案 1 :(得分:1)

我要做的是将所有操作包装在Callable中,将它们提交给ExecutorService,将返回的Future存储在列表中。

然后当它们全部完成时,在主线程中填充结果列表。

答案 2 :(得分:0)

为了在项目为外部时继续运行,操作call service应该启动某种异步活动(例如,ThreadRunnable提交给某种Executor 1}})。此活动应具有回调机制,以便在get result完成后,它可以执行任务的最后一部分:populate Response and add to List

您必须同步List的访问权限或安排所有访问权限为单线程。