Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does thenRunAsync (as opposed to thenRun) make any difference if chained after a runAsync call?

In the following code, does it make any difference to call thenRunAsync? Should I just call thenRun instead?

CompletableFuture.runAsync(this::doWork , executorService)
     .thenRunAsync(this::handleSuccess);

Elaborating in response to the comments: If I were to use this code instead,

CompletableFuture.runAsync(this::doWork , executorService)
     .thenRun(this::handleSuccess);

would it behave any differently?

In both cases, the behavior is non-blocking, and the second task won't run until the first completes, as far as I understand it, anyhow.

like image 564
April Papajohn Avatar asked Apr 06 '16 21:04

April Papajohn


People also ask

What is difference between supplyAsync and runAsync?

The difference between runAsync() and supplyAsync() is that the former returns a Void while supplyAsync() returns a value obtained by the Supplier. Both methods also support a second input argument — a custom Executor to submit tasks to.

How to use CompletableFuture runAsync?

If you want to run some background task asynchronously and don't want to return anything from the task, then you can use CompletableFuture. runAsync() method. It takes a Runnable object and returns CompletableFuture<Void> . // Using Lambda Expression CompletableFuture<Void> future = CompletableFuture.


1 Answers

The method thenRun allows the execution of the Runnable directly in the caller’s thread, if the CompletableFuture has already completed. Since even in a direct invocation chain like CompletableFuture.runAsync(…).thenRun(…); there’s the possibility that the asynchronous task has already completed by the time thenRun is invoked, there’s the possibility that the dependent action is executed in the caller’s thread, unlike thenRunAsync which will always use the default (or provided) executor.

So in one sentence, yes, it makes a difference.


By the way, using thenRunAsync (the single argument version) will not execute the action using the Executor supplied to the initial factory call, but the default Executor.

You can easily compare the different behaviors:

public static void main(String[] args) {
    ExecutorService e=Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
    CompletableFuture<?> f=CompletableFuture.runAsync(()->{}, e);
    f.join();
    f.thenRun(()->System.out.println("thenRun:\t"+Thread.currentThread()));
    f.thenRunAsync(()->System.out.println("thenRunAsync:\t"+Thread.currentThread()));
    f.thenRunAsync(()->System.out.println("thenRunAsync+e:\t"+Thread.currentThread()), e);
    e.shutdown();
}

will print

thenRun:        Thread[main,5,main]
thenRunAsync:   Thread[ForkJoinPool.commonPool-worker-1,5,main]
thenRunAsync+e: Thread[sole thread,5,main]

whereas

public static void main(String[] args) {
   ExecutorService e=Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
   CompletableFuture<?>f=CompletableFuture.runAsync(()->LockSupport.parkNanos((int)1e9),e);
   f.thenRun(()->System.out.println("thenRun:\t"+Thread.currentThread()));
   f.thenRunAsync(()->System.out.println("thenRunAsync:\t"+Thread.currentThread()));
   f.thenRunAsync(()->System.out.println("thenRunAsync+e:\t"+Thread.currentThread()), e);
   LockSupport.parkNanos((int)2e9);
   e.shutdown();
}

will print

thenRun:        Thread[sole thread,5,main]
thenRunAsync:   Thread[ForkJoinPool.commonPool-worker-1,5,main]
thenRunAsync+e: Thread[sole thread,5,main]

So thenRun may execute the action in either, the caller’s thread or the Executor’s thread whereas the single-argument thenRunAsync will always use the Fork/Join pool and only the two argument thenRunAsync will always use the provided executor.

like image 162
Holger Avatar answered Oct 30 '22 20:10

Holger