首页 > 解决方案 > CompletableFuture: proper way to run a list of futures, wait for result and handle exception

问题描述

I have a legacy code which have dozen database calls to populate a report, it takes noticeable amount of time which I try to reduce using CompletableFuture.

I have some doubts that I do things correctly and not overuse this technology.

My code now looks like this:

  1. Start asynchronous population of document sections with many database calls inside each methods

    CompletableFuture section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
    CompletableFuture section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
        ...
    CompletableFuture section1oFuture = CompletableFuture.supplyAsync(() -> populateSection10(arguments));
    
  2. Then I'm arranging futures in specific order in arrayList and joining all of them to make sure that my code will run further only when all futures are finished.

    List<CompletableFuture> futures = Arrays.asList(
                section1Future,
                section2Future, ...
                section10Future);
    
    List<Object> futureResults = futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    
  3. Then I'm populating PDF document itself with its pieces

    Optional.ofNullable((PdfPTable) futureResults.get(0)).ifPresent(el -> populatePdfElement(document, el));
    Optional.ofNullable((PdfPTable) futureResults.get(1)).ifPresent(el -> populatePdfElement(document, el));
        ...
    Optional.ofNullable((PdfPTable) futureResults.get(10)).ifPresent(el -> populatePdfElement(document, el));
    

    return document

My concerns are:

1) Is it okay to create and instantiate many Completable Futures in such way? Order them in required sequence in arrayList, join them to make sure that they are all finished, and then get result by casting them into specific object?

2) Is it okay to run without specifying an executor service but to rely on common ForkJoinPool? However this code runs in web container, so probably in order to use JTA I need to use container provided thread pool executor via JNDI?

3) If this code is surrounded in try-catch I should be able to catch CompletionException in main thread, right? Or In order to do that I should declare each features like following:

CompletableFuture.supplyAsync(() -> populateSection1(arguments))
    .exceptionally (ex -> {
                    throw new RuntimeException(ex.getCause());
        });

4) Is it possible to overuse CompletableFutures so they become a performance bottleneck itself? Like many futures waits one executor to start running? How to avoid that? Use container provided executor service? If yes, could someone please point me to some best practice on how to correctly configure executor service taking to account processors and memory amount?

5) A memory impact. I read in parallel thread that there can be a problem with OOME as many object are created and garbage collected. Is there a best practice on how to calculate correct amount of memory required for application?

标签: javaasynchronousdesign-patternsjava-8completable-future

解决方案



推荐阅读