CompletableFuture: proper way to run a list of futures, wait for result and handle exception









up vote
3
down vote

favorite












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?










share|improve this question









New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
    – Alexei Kaigorodov
    Nov 10 at 15:51










  • Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
    – AnMi
    Nov 10 at 16:59














up vote
3
down vote

favorite












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?










share|improve this question









New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
    – Alexei Kaigorodov
    Nov 10 at 15:51










  • Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
    – AnMi
    Nov 10 at 16:59












up vote
3
down vote

favorite









up vote
3
down vote

favorite











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?










share|improve this question









New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











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?







java asynchronous design-patterns java-8 completable-future






share|improve this question









New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited Nov 10 at 14:56









Mikhail Kholodkov

3,51842141




3,51842141






New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked Nov 10 at 13:13









AnMi

161




161




New contributor




AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






AnMi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











  • what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
    – Alexei Kaigorodov
    Nov 10 at 15:51










  • Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
    – AnMi
    Nov 10 at 16:59
















  • what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
    – Alexei Kaigorodov
    Nov 10 at 15:51










  • Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
    – AnMi
    Nov 10 at 16:59















what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
– Alexei Kaigorodov
Nov 10 at 15:51




what you really want is to parallelize your program in hope it will run faster. So first you need to depict a schema of your parallelized program, and only then choose the most appropriate tools to implement it. Besides CompletableFuture, there exist threads, parallel and reactive streams, and actors, to name a few. Are you sure CompletableFuture is the best tool for your task?
– Alexei Kaigorodov
Nov 10 at 15:51












Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
– AnMi
Nov 10 at 16:59




Thanks for reply Alexei! Yes I try to parallelize in order to speed up an already written Spring monolith. I'm using CompletableFuture because I didn't have a chance to work with other asynchronous stuff except Play! framework which AFAIK is backed by CF. But if to abstract from application is it okay to instantiate, run, join and get result of CompletableFuture in this way? Am I wrong saying that CompletableFuture is aimed to ease developer life using it instead of threads? P.S I'm not saying that I'm not interested or scary to learn best practices of pure multithreading
– AnMi
Nov 10 at 16:59












1 Answer
1






active

oldest

votes

















up vote
1
down vote













The approach is not wrong in general, but there are things to improve.



Most notably, you should not use raw types, like CompletableFuture.



When populateSection… returns a PdfPTable, you should use use CompletableFuture<PdfPTable> consistently throughout the code.



I.e.



CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
...
CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));


even if these methods do not declare the return type you are assuming to be always returned at runtime, you should insert the type cast at this early stage:



CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
...
CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));


Then, you can use



Stream.of(section1Future, section2Future, ..., section10Future)
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.forEachOrdered(el -> populatePdfElement(document, el));


By not using raw types, you already get the desired result type and you can do the 3rd step’s operations, i.e. filtering and performing the final action, right in this stream operation.



If you still need the list, you may use



List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList());

results.forEach(el -> populatePdfElement(document, el));



That said, the parallelism depends on the thread pool used for the operation (specified to supplyAsync). When you don’t specify an executor, you get the default Fork/Join pool used by parallel streams, so in this specific case, you get the same result much simpler as



List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
() -> populateSection1(arguments),
() -> populateSection2(arguments));
...
() -> populateSection10(arguments)))
.parallel()
.map(Supplier::get)
.filter(Objects::nonNull)
.forEachOrdered(el -> populatePdfElement(document, el));


or



List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
() -> populateSection1(arguments),
() -> populateSection2(arguments));
...
() -> populateSection10(arguments)))
.parallel()
.map(Supplier::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());

results.forEach(el -> populatePdfElement(document, el));


While both variants ensure that populatePdfElement will be called in the right order and one at a time, only the latter will perform all calls from the initiating thread.



Regarding exception handling, you’ll get any exception thrown by a supplier wrapped in a CompletionException when you call CompletableFuture::join. Chaining something like .exceptionally (ex -> throw new RuntimeException(ex.getCause()); ); makes no sense, the new RuntimeException will also be wrapped in a CompletionException when you call CompletableFuture::join.



In the Stream variant, you’ll get the exception without a wrapper. Since Supplier does not allow checked exceptions, only subtypes of RuntimeException or Error are possible.



The other questions are too broad for the Q&A.






share|improve this answer




















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );






    AnMi is a new contributor. Be nice, and check out our Code of Conduct.









     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53239294%2fcompletablefuture-proper-way-to-run-a-list-of-futures-wait-for-result-and-hand%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    The approach is not wrong in general, but there are things to improve.



    Most notably, you should not use raw types, like CompletableFuture.



    When populateSection… returns a PdfPTable, you should use use CompletableFuture<PdfPTable> consistently throughout the code.



    I.e.



    CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
    CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
    ...
    CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));


    even if these methods do not declare the return type you are assuming to be always returned at runtime, you should insert the type cast at this early stage:



    CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
    CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
    ...
    CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));


    Then, you can use



    Stream.of(section1Future, section2Future, ..., section10Future)
    .map(CompletableFuture::join)
    .filter(Objects::nonNull)
    .forEachOrdered(el -> populatePdfElement(document, el));


    By not using raw types, you already get the desired result type and you can do the 3rd step’s operations, i.e. filtering and performing the final action, right in this stream operation.



    If you still need the list, you may use



    List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
    .map(CompletableFuture::join)
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

    results.forEach(el -> populatePdfElement(document, el));



    That said, the parallelism depends on the thread pool used for the operation (specified to supplyAsync). When you don’t specify an executor, you get the default Fork/Join pool used by parallel streams, so in this specific case, you get the same result much simpler as



    List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
    () -> populateSection1(arguments),
    () -> populateSection2(arguments));
    ...
    () -> populateSection10(arguments)))
    .parallel()
    .map(Supplier::get)
    .filter(Objects::nonNull)
    .forEachOrdered(el -> populatePdfElement(document, el));


    or



    List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
    () -> populateSection1(arguments),
    () -> populateSection2(arguments));
    ...
    () -> populateSection10(arguments)))
    .parallel()
    .map(Supplier::get)
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

    results.forEach(el -> populatePdfElement(document, el));


    While both variants ensure that populatePdfElement will be called in the right order and one at a time, only the latter will perform all calls from the initiating thread.



    Regarding exception handling, you’ll get any exception thrown by a supplier wrapped in a CompletionException when you call CompletableFuture::join. Chaining something like .exceptionally (ex -> throw new RuntimeException(ex.getCause()); ); makes no sense, the new RuntimeException will also be wrapped in a CompletionException when you call CompletableFuture::join.



    In the Stream variant, you’ll get the exception without a wrapper. Since Supplier does not allow checked exceptions, only subtypes of RuntimeException or Error are possible.



    The other questions are too broad for the Q&A.






    share|improve this answer
























      up vote
      1
      down vote













      The approach is not wrong in general, but there are things to improve.



      Most notably, you should not use raw types, like CompletableFuture.



      When populateSection… returns a PdfPTable, you should use use CompletableFuture<PdfPTable> consistently throughout the code.



      I.e.



      CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
      CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
      ...
      CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));


      even if these methods do not declare the return type you are assuming to be always returned at runtime, you should insert the type cast at this early stage:



      CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
      CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
      ...
      CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));


      Then, you can use



      Stream.of(section1Future, section2Future, ..., section10Future)
      .map(CompletableFuture::join)
      .filter(Objects::nonNull)
      .forEachOrdered(el -> populatePdfElement(document, el));


      By not using raw types, you already get the desired result type and you can do the 3rd step’s operations, i.e. filtering and performing the final action, right in this stream operation.



      If you still need the list, you may use



      List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
      .map(CompletableFuture::join)
      .filter(Objects::nonNull)
      .collect(Collectors.toList());

      results.forEach(el -> populatePdfElement(document, el));



      That said, the parallelism depends on the thread pool used for the operation (specified to supplyAsync). When you don’t specify an executor, you get the default Fork/Join pool used by parallel streams, so in this specific case, you get the same result much simpler as



      List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
      () -> populateSection1(arguments),
      () -> populateSection2(arguments));
      ...
      () -> populateSection10(arguments)))
      .parallel()
      .map(Supplier::get)
      .filter(Objects::nonNull)
      .forEachOrdered(el -> populatePdfElement(document, el));


      or



      List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
      () -> populateSection1(arguments),
      () -> populateSection2(arguments));
      ...
      () -> populateSection10(arguments)))
      .parallel()
      .map(Supplier::get)
      .filter(Objects::nonNull)
      .collect(Collectors.toList());

      results.forEach(el -> populatePdfElement(document, el));


      While both variants ensure that populatePdfElement will be called in the right order and one at a time, only the latter will perform all calls from the initiating thread.



      Regarding exception handling, you’ll get any exception thrown by a supplier wrapped in a CompletionException when you call CompletableFuture::join. Chaining something like .exceptionally (ex -> throw new RuntimeException(ex.getCause()); ); makes no sense, the new RuntimeException will also be wrapped in a CompletionException when you call CompletableFuture::join.



      In the Stream variant, you’ll get the exception without a wrapper. Since Supplier does not allow checked exceptions, only subtypes of RuntimeException or Error are possible.



      The other questions are too broad for the Q&A.






      share|improve this answer






















        up vote
        1
        down vote










        up vote
        1
        down vote









        The approach is not wrong in general, but there are things to improve.



        Most notably, you should not use raw types, like CompletableFuture.



        When populateSection… returns a PdfPTable, you should use use CompletableFuture<PdfPTable> consistently throughout the code.



        I.e.



        CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
        CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
        ...
        CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));


        even if these methods do not declare the return type you are assuming to be always returned at runtime, you should insert the type cast at this early stage:



        CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
        CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
        ...
        CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));


        Then, you can use



        Stream.of(section1Future, section2Future, ..., section10Future)
        .map(CompletableFuture::join)
        .filter(Objects::nonNull)
        .forEachOrdered(el -> populatePdfElement(document, el));


        By not using raw types, you already get the desired result type and you can do the 3rd step’s operations, i.e. filtering and performing the final action, right in this stream operation.



        If you still need the list, you may use



        List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
        .map(CompletableFuture::join)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

        results.forEach(el -> populatePdfElement(document, el));



        That said, the parallelism depends on the thread pool used for the operation (specified to supplyAsync). When you don’t specify an executor, you get the default Fork/Join pool used by parallel streams, so in this specific case, you get the same result much simpler as



        List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
        () -> populateSection1(arguments),
        () -> populateSection2(arguments));
        ...
        () -> populateSection10(arguments)))
        .parallel()
        .map(Supplier::get)
        .filter(Objects::nonNull)
        .forEachOrdered(el -> populatePdfElement(document, el));


        or



        List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
        () -> populateSection1(arguments),
        () -> populateSection2(arguments));
        ...
        () -> populateSection10(arguments)))
        .parallel()
        .map(Supplier::get)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

        results.forEach(el -> populatePdfElement(document, el));


        While both variants ensure that populatePdfElement will be called in the right order and one at a time, only the latter will perform all calls from the initiating thread.



        Regarding exception handling, you’ll get any exception thrown by a supplier wrapped in a CompletionException when you call CompletableFuture::join. Chaining something like .exceptionally (ex -> throw new RuntimeException(ex.getCause()); ); makes no sense, the new RuntimeException will also be wrapped in a CompletionException when you call CompletableFuture::join.



        In the Stream variant, you’ll get the exception without a wrapper. Since Supplier does not allow checked exceptions, only subtypes of RuntimeException or Error are possible.



        The other questions are too broad for the Q&A.






        share|improve this answer












        The approach is not wrong in general, but there are things to improve.



        Most notably, you should not use raw types, like CompletableFuture.



        When populateSection… returns a PdfPTable, you should use use CompletableFuture<PdfPTable> consistently throughout the code.



        I.e.



        CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
        CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
        ...
        CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));


        even if these methods do not declare the return type you are assuming to be always returned at runtime, you should insert the type cast at this early stage:



        CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
        CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
        ...
        CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));


        Then, you can use



        Stream.of(section1Future, section2Future, ..., section10Future)
        .map(CompletableFuture::join)
        .filter(Objects::nonNull)
        .forEachOrdered(el -> populatePdfElement(document, el));


        By not using raw types, you already get the desired result type and you can do the 3rd step’s operations, i.e. filtering and performing the final action, right in this stream operation.



        If you still need the list, you may use



        List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
        .map(CompletableFuture::join)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

        results.forEach(el -> populatePdfElement(document, el));



        That said, the parallelism depends on the thread pool used for the operation (specified to supplyAsync). When you don’t specify an executor, you get the default Fork/Join pool used by parallel streams, so in this specific case, you get the same result much simpler as



        List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
        () -> populateSection1(arguments),
        () -> populateSection2(arguments));
        ...
        () -> populateSection10(arguments)))
        .parallel()
        .map(Supplier::get)
        .filter(Objects::nonNull)
        .forEachOrdered(el -> populatePdfElement(document, el));


        or



        List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
        () -> populateSection1(arguments),
        () -> populateSection2(arguments));
        ...
        () -> populateSection10(arguments)))
        .parallel()
        .map(Supplier::get)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

        results.forEach(el -> populatePdfElement(document, el));


        While both variants ensure that populatePdfElement will be called in the right order and one at a time, only the latter will perform all calls from the initiating thread.



        Regarding exception handling, you’ll get any exception thrown by a supplier wrapped in a CompletionException when you call CompletableFuture::join. Chaining something like .exceptionally (ex -> throw new RuntimeException(ex.getCause()); ); makes no sense, the new RuntimeException will also be wrapped in a CompletionException when you call CompletableFuture::join.



        In the Stream variant, you’ll get the exception without a wrapper. Since Supplier does not allow checked exceptions, only subtypes of RuntimeException or Error are possible.



        The other questions are too broad for the Q&A.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 12 at 13:41









        Holger

        158k22216416




        158k22216416




















            AnMi is a new contributor. Be nice, and check out our Code of Conduct.









             

            draft saved


            draft discarded


















            AnMi is a new contributor. Be nice, and check out our Code of Conduct.












            AnMi is a new contributor. Be nice, and check out our Code of Conduct.











            AnMi is a new contributor. Be nice, and check out our Code of Conduct.













             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53239294%2fcompletablefuture-proper-way-to-run-a-list-of-futures-wait-for-result-and-hand%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            How to read a connectionString WITH PROVIDER in .NET Core?

            In R, how to develop a multiplot heatmap.2 figure showing key labels successfully

            Museum of Modern and Contemporary Art of Trento and Rovereto