我要从中并行请求不同的数据源(因为每个请求都是一个http调用,可能会非常耗时)。但是我将仅使用这些请求的1个响应。因此,我对它们进行了优先排序。如果第一个响应无效,我将检查第二个响应。如果它也无效,我想使用第三个,依此类推。 但是,我想在收到第一个正确的响应后立即停止处理并返回结果。
为了模拟该问题,我创建了以下代码,其中尝试使用Java并行流。但是问题是我只有在处理完所有请求后才能收到最终结果。
mongod
因此,当我使用以下命令执行程序时:
public class ParallelExecution {
private static Supplier<Optional<Integer>> testMethod(String strInt) {
return () -> {
Optional<Integer> result = Optional.empty();
try {
result = Optional.of(Integer.valueOf(strInt));
System.out.printf("converted string %s to int %d\n",
strInt,
result.orElse(null));
} catch (NumberFormatException ex) {
System.out.printf("CANNOT CONVERT %s to int\n", strInt);
}
try {
int randomValue = result.orElse(10000);
TimeUnit.MILLISECONDS.sleep(randomValue);
System.out.printf("converted string %s to int %d in %d milliseconds\n",
strInt,
result.orElse(null), randomValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
};
}
public static void main(String[] args) {
Instant start = Instant.now();
System.out.println("Starting program: " + start.toString());
List<Supplier<Optional<Integer>>> listOfFunctions = new ArrayList();
for (String arg: args) {
listOfFunctions.add(testMethod(arg));
}
Integer value = listOfFunctions.parallelStream()
.map(function -> function.get())
.filter(optValue -> optValue.isPresent()).map(val-> {
System.out.println("************** VAL: " + val);
return val;
}).findFirst().orElse(null).get();
Instant end = Instant.now();
Long diff = end.toEpochMilli() - start.toEpochMilli();
System.out.println("final value:" + value + ", worked during " + diff + "ms");
}
}
我希望尽快(大约在34毫秒后)获得结果“ 34”,但实际上,我正在等待10秒钟以上。
您能帮忙找到最有效的解决方案吗?
答案 0 :(得分:2)
ExecutorService#invokeAny
看起来是个不错的选择。
List<Callable<Optional<Integer>>> tasks = listOfFunctions
.stream()
.<Callable<Optional<Integer>>>map(f -> f::get)
.collect(Collectors.toList());
ExecutorService service = Executors.newCachedThreadPool();
Optional<Integer> value = service.invokeAny(tasks);
service.shutdown();
我将您的List<Supplier<Optional<Integer>>>
转换为List<Callable<Optional<Integer>>>
,以便能够在invokeAny
中传递它。您可以最初构建Callable
。然后,我创建了ExecutorService
并提交了任务。
从任务返回结果后,将立即返回第一个成功执行的任务的结果。其他任务最终将被中断。
您可能还想研究CompletionService
。
List<Callable<Optional<Integer>>> tasks = Arrays
.stream(args)
.<Callable<Optional<Integer>>>map(arg -> () -> testMethod(arg).get())
.collect(Collectors.toList());
final ExecutorService underlyingService = Executors.newCachedThreadPool();
final ExecutorCompletionService<Optional<Integer>> service = new ExecutorCompletionService<>(underlyingService);
tasks.forEach(service::submit);
Optional<Integer> value = service.take().get();
underlyingService.shutdownNow();
答案 1 :(得分:0)
您可以使用队列将结果放入:
private static void testMethod(String strInt, BlockingQueue<Integer> queue) {
// your code, but instead of returning anything:
result.ifPresent(queue::add);
}
然后用
调用for (String s : args) {
CompletableFuture.runAsync(() -> testMethod(s, queue));
}
Integer result = queue.take();
请注意,这只会处理第一个结果,如您的示例中一样。
答案 2 :(得分:0)
我已经使用competableFutures和anyOf方法尝试过。当将来的任何一个完成时,它将返回。现在,停止其他任务的关键是为completableFuture提供自己的执行服务,并在需要时将其关闭。
public static void main(String[] args) {
Instant start = Instant.now();
System.out.println("Starting program: " + start.toString());
CompletableFuture<Optional<Integer>> completableFutures[] = new CompletableFuture[args.length];
ExecutorService es = Executors.newFixedThreadPool(args.length,r -> {
Thread t = new Thread(r);
t.setDaemon(false);
return t;
});
for (int i = 0;i < args.length; i++) {
completableFutures[i] = CompletableFuture.supplyAsync(testMethod(args[i]),es);
}
CompletableFuture.anyOf(completableFutures).
thenAccept(res-> {
System.out.println("Result - " + res + ", Time Taken : " + (Instant.now().toEpochMilli()-start.toEpochMilli()));
es.shutdownNow();
});
}
PS:它将抛出中断的异常,您可以在try catch块中忽略该异常,而不打印堆栈跟踪。此外,理想情况下,线程池的大小应与args数组的长度相同。