Functionally, the two approaches are more or less the same:
- you submit your tasks for execution;
- you don’t wait for the result.
Technically, however, there are some subtle differences:
- In the second approach, you didn’t specify an executor, so it will use the common
ForkJoinPool
. You would have to pass an executor as second argument ofsupplyAsync()
if you don’t want that; - The
CompletableFuture
API allows to easily chain more calls withthenApply()
,thenCompose()
etc. It is thus more flexible than the simpleFuture
returned byExecutorService.submit()
; - Using
CompletableFuture
allows to easily return a future from yourchild()
method usingreturn CompletableFuture.allOf(the previously created futures)
.
Concerning readability, it’s a matter of preference, but if you want equivalent code the CompletableFuture
approach might be considered a bit less readable once you have formatted it similarly. Compare:
executorService.submit(MyFileService::service1);
executorService.submit(MyFileService::service2);
executorService.submit(MyFileService::service3);
with
CompletableFuture.supplyAsync(MyFileService::service1, executorService);
CompletableFuture.supplyAsync(MyFileService::service2, executorService);
CompletableFuture.supplyAsync(MyFileService::service3, executorService);