13

I have the need to create an asynchronous, non-blocking task in Java 8, I would like to use CompletableFutures but I'm not sure if it fulfils my needs.

To simplify the case, let's say we have an API that retrieve some data for the user but at the same time want to start a separate task to do some operations. I don't need and don't want to wait for this separate task to finish, I want to send the response right away to the user. An example in mock code:

public Response doSomething(params) {
  Object data = retrieveSomeData(params);

  // I don't want to wait for this to finish, I don't care if it succeeds or not
  doSomethingNoWait(data);

  return new Response(data);
}

I was looking at CompletableFutures, something like this:

CompletableFuture.supplyAsync(this::doSomethingNoWait)  
             .thenApply(this::logSomeMessage); 

What I would like to know is if that is the correct approach? Would the response be returned to the user before doSomethingNoWait finish what it has to do?

Thanks!

malavock
  • 341
  • 1
  • 5
  • 21

2 Answers2

13

Great question. Yes a CompleteableFuture is perfect for your needs! Lets investigate further the functionality of the class.

A CompleteableFuture is a wrapper for the Future class and allows for executing in parallel. Lets take a look at an example from this article.

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
});

In the above example, the program will start the CompletableFuture asynchronously and will have the new thread sleep in the background. If we add .thenApply() seen below:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
}).thenApply(result -> {
   System.out.println(result);
   return "Result of the then apply";
});

The application will execute just as we discussed before, but once it completes (non-exceptionally) the current thread running the supplyAsync will execute the print statement.

Note, if a synchronous transformation is added to a future after it has finished execution, the calling thread will perform the transformation. As we see below:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
});
// Long calculations that allows the future above to finish
future.thenApply(result -> {
   System.out.println(result);
   return "Result of the then apply";
});

the thenApply will run on the thread that runs future.thenApply(...), not the thread spawned to run the supplyAsync in the background.

Impurity
  • 1,037
  • 2
  • 16
  • 31
  • Sounds perfect, I somehow missed the thenApplyAsync method. Thanks! – malavock Aug 18 '18 at 16:18
  • missing return statement after `System.out.println(result);` – Kaigo Mar 10 '20 at 10:30
  • "the calling thread will execute the print statement". Which would that be? Are you saying you know which thread will execute `thenApply`? – Eugene Jan 09 '21 at 00:36
  • I have attempted to clarify my answer with the most recent edit – Impurity Jan 09 '21 at 02:25
  • The fact that it delegates async tasks to a new thread(Executor Instances), it is still not completely non-blocking/Event-driven approach right? The executor threads are blocked for any blocking call and subsequent tasks are qued to the threadPool right? So it would be more of parallel execution than non-blocking execution. Hope my comprehension is correct on this! @Impurity – Deekshith Anand Sep 19 '21 at 17:07
  • It certainly is non-blocking/parallel. The parallel threads managed by a ThreadPool will only be blocked if you wait for them in another thread (e.g. forkJoin). Sure, the implementation may include some level of queues, but you only have so many core/threads. This question was not related to event-driven approaches, rather on how to create an asynchronous, non-blocking tasks in Java. CompleteableFutures (and their underlying thread pools implementations) are a great way to achieve asynchronous, non-blocking tasks in Java. – Impurity Sep 20 '21 at 21:52
0

"thenApply(this::logSomeMessage)" will get executed only when "supplyAsync(this::doSomethingNoWait)" stage completes normally.

You could instead do this:

CompletableFuture.supplyAsync(this::doSomethingNoWait)  
logSomeMessage()
RCT
  • 1,044
  • 1
  • 5
  • 12