Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExecutorService vs CompletableFuture

I have been trying to implement an asynchronous process, where the parent method calls a child method which would in-turn call three different methods. I want all of this process to be done asynchronously i.e. after these three calls in the child method are made in parallel the control should go back to the parent method and continue with the rest of its execution.

I have this code which when tested works fine.

public ReturnSomething parent(){
    child();
    ...//rest to UI
}

private void child(){
  ExecutorService executorService = Executors.newFixedThreadPool(3);

  Runnable service1 = () -> {
     MyFileService.service1();
  };

  Runnable service2 = () -> {
      MyFileService.service2();
  };

  Runnable service3 = () -> {
      MyFileService.service3();
  };

  executorService.submit(service1);
  executorService.submit(service2);
  executorService.submit(service3);
}

Now, my lead is asking me to use this rather.

public ReturnSomething parent(){
    child();
    ...//rest to UI
}

private void child(){
    CompletableFuture.supplyAsync(() ->  MyFileService.service1();
    CompletableFuture.supplyAsync(() ->  MyFileService.service2();
    CompletableFuture.supplyAsync(() ->  MyFileService.service3();
}

I understand that that CompletableFuture is new from Java 8, but how is the 2nd code better than the 1st? Since, for ExecutorService, I am not calling the "get()" method I would not be waiting for the aysnc response. So, can some one please explain what is the difference?

like image 929
Dwarak Avatar asked Sep 12 '18 21:09

Dwarak


People also ask

What is difference between ExecutorService and CompletableFuture?

The ExecutorService executes a task and updates the result in the Future. A CompletableFuture introduced later in Java 8 implements the Future interface. So CompletableFuture contains all the functionalities provided by the Future interface. The CompletableFuture allows you to chain tasks together.

What is an ExecutorService?

public interface ExecutorService extends Executor. An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks. An ExecutorService can be shut down, which will cause it to reject new tasks.

What is the difference between Executor and ExecutorService?

Executor just executes stuff you give it. ExecutorService adds startup, shutdown, and the ability to wait for and look at the status of jobs you've submitted for execution on top of Executor (which it extends). This is a perfect answer, short and clear.

Why ExecutorService is used in Java?

The ExecutorService helps in maintaining a pool of threads and assigns them tasks. It also provides the facility to queue up tasks until there is a free thread available if the number of tasks is more than the threads available.


2 Answers

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 of supplyAsync() if you don't want that;
  • The CompletableFuture API allows to easily chain more calls with thenApply(), thenCompose() etc. It is thus more flexible than the simple Future returned by ExecutorService.submit();
  • Using CompletableFuture allows to easily return a future from your child() method using return 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);
like image 51
Didier L Avatar answered Oct 29 '22 03:10

Didier L


You're not waiting for results in both cases.

The advantage of the second approach is simply less boilerplate. That's what runAsync() and supplyAsync() are good for.

But if you don't actually return any value, you should use runAsync()

What second approach also provides, is the ability to wait for all futures with CompletableFuture.allOf(). Which is also doesn't exist in the first scenario.

like image 2
Alexey Soshin Avatar answered Oct 29 '22 03:10

Alexey Soshin