2

I want to load data on the application startup by calling different services which returns lists of different object types. Each service calls DAO class to read some data. So I have created individual CompletableFuture like:

CompletableFuture<List<DTO1> future1 = CompletableFuture.supplyAsync(() -> service1.callMethod1());
CompletableFuture<List<DTO2> future2 = CompletableFuture.supplyAsync(() -> service2.callMethod2());
CompletableFuture<List<DTO3> future3 = CompletableFuture.supplyAsync(() -> service3.callMethod3());

and combined them like:

CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);

If I do something like:

combinedFuture.thenRunAsync(()->future1.join(), future2.join(), future3.join())

Then I am getting the compile time error. I am new to the Java 8 concurrency features such as CompletableFuture, I thought calling thenRunAsync will run each future1,future2,future3 in different threads. Is it correct way to do it by calling thenRunAsync? If Yes, then how to remove that compile time error?

But if I do something like:

Stream.of(future1, future2, future3)
        .map(CompletableFuture::join)
        .collect(Collectors.toList());

It is returning List<? extends List<?>> then It seems to work but should I call .collect(Collectors.toList()) ? I am not concerned about this combined result as each of the individual method calls are already annotated with @Cacheable for caching purposes. Also, If I do something like:

Stream.of(future1, future2, future3)
        .foreach(CompletableFuture::join);

will it run each future1,future2,future3 in different threads? How can I run future1,future2,future3 in 3 different threads? Is there any correct way to run these three database calls asynchronously?

Mohsin M
  • 239
  • 7
  • 20
  • Do those service calls really need to be wrapped in a `CompletableFuture`? – Andrew S Jan 07 '22 at 15:32
  • @andrew-s No, originally those methods are returning `List`. I have wrapped them `CompletableFuture` and combined them to make async calls, not sure if that's the correct way – Mohsin M Jan 07 '22 at 15:36
  • To clarify: why not just `service1.callMethod1()` ? Why wrap in a `CompletableFuture`? Maybe [this](https://stackoverflow.com/questions/27940704/how-to-load-cache-on-startup-in-spring) will help too. – Andrew S Jan 07 '22 at 15:53
  • @andrew-s As I said, as per my limited knowledge I have wrapped them in `CompletableFuture` to call them on different thread and If I do `service1.callMethod1()` then it will be called on same thread. – Mohsin M Jan 07 '22 at 16:47

1 Answers1

3
CompletableFuture<List<DTO1> future1 = CompletableFuture.supplyAsync(() -> service1.callMethod1());
CompletableFuture<List<DTO2> future2 = CompletableFuture.supplyAsync(() -> service2.callMethod2());
CompletableFuture<List<DTO3> future3 = CompletableFuture.supplyAsync(() -> service3.callMethod3());

This code is already instructing all three futures to run on different threads (assuming that there are at least 3 threads in the common pool). Calling join() on one of them simply instructs the calling thread to block on completion of that task, and doesn't change the fact that the 3 calls are proceeding in parallel. All of your solutions that compile are reasonable.

MikeFHay
  • 8,562
  • 4
  • 31
  • 52
  • 3
    But when calling `join` only for the purpose of waiting for the completion, `CompletableFuture.allOf(future1, future2, future3).join();` is simpler than the other approaches. And even `future1.join(); future2.join(); future3.join();` would do… – Holger Jan 10 '22 at 14:59