繁体   English   中英

如何在java中以最有效和优雅的方式使用并行处理

[英]How to use parallel processing in most efficient and elegant way in java

我有不同的数据源,我想要并行请求(因为每个请求都是一个http调用,可能非常耗时)。 但是我将只使用这些请求中的1个响应。 所以我有点优先考虑它们。 如果第一个响应无效,我将检查第二个响应。 如果它也无效我想使用第三个,等等。但是我想在收到第一个正确的响应后立即停止处理并返回结果。

为了模拟这个问题,我创建了以下代码,我正在尝试使用java并行流。 但问题是我只在处理完所有请求后才收到最终结果。

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");
    }
}

所以当我使用以下命令执行程序时:

$java ParallelExecution dfafj 34 1341 4656 dfad 245df 5767

我希望尽快得到结果“34”(大约在34毫秒后),但事实上,我等待的时间超过10秒。

你能帮忙找到解决这个问题的最有效方法吗?

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();

您可以使用队列将结果放入:

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();

请注意,这只会处理第一个结果,如样本中所示。

我已经尝试过使用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数组的长度相同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM