我正在处理一个返回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是程序性的和阻塞的,我想要的是一种非阻塞设计,这样,如果该项目是外部激活该呼叫并继续列表中的下一项。然后当循环完成时,我可以等待所有完成。
这样做的好方法是什么?我还考虑按责任创建单独的课程。
编辑:引入的原因是减少延迟命中率。
答案 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));
}
}
现在您可以简单地遍历futures
。 AsyncResult
将立即返回,因为该值在创建时已经是计算机(synchResponse
)。但是你必须等待从线程池返回的Future
。
请记住,Future.get()
允许您检索原始异常。此外,列表中Future
的顺序与原始项的顺序相同,因此如果第Future
个失败,items
列表中的第n个项目就是原因。
答案 1 :(得分:1)
我要做的是将所有操作包装在Callable
中,将它们提交给ExecutorService
,将返回的Future
存储在列表中。
然后当它们全部完成时,在主线程中填充结果列表。
答案 2 :(得分:0)
为了在项目为外部时继续运行,操作call service
应该启动某种异步活动(例如,Thread
或Runnable
提交给某种Executor
1}})。此活动应具有回调机制,以便在get result
完成后,它可以执行任务的最后一部分:populate Response and add to List
。
您必须同步List
的访问权限或安排所有访问权限为单线程。