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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With