
1. Overview
In this article, we will discuss the runAsync and supplyAsync methods of the CompletableFuture interface. This CompletableFuture interface was added to the java.util.concurrent
package in the Java 8 release.
2. CompletableFuture runAsync and supplyAsync
You can use the CompletableFuture
interface for asynchronous programming. In other words, this interface runs the non-blocking code as a task in a non-blocking thread. After execution, it notifies the caller thread about the task progress, completion, or any failure.
This CompletableFuture
has the following methods to execute the code:
1. supplyAsync
This method takes a Supplier<U>
as an argument and returns the CompletableFuture<U>
instance back to the caller thread. A supplier is nothing but a function that contains the code to be executed asynchronously. This function returns a value that you can retrieve later by using the CompletableFuture<U>
instance.
2. RunAsync
This method accepts a Runnable
as an input parameter and returns CompletableFuture<Void>
. If you notice, the type of the CompletableFuture
is void
, meaning this method won’t return any result back to the caller.
We have seen the differences between the supplyAsync and runAsync. Now let’s understand each of these functions with examples.
2.1. CompletableFuture supplyAsync with common pool
The supplyAsync method uses the common pool if no executor is specified:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
The above supplyAsync
uses the threads from the common pool (ForkJoinPool#commonPool
) to execute the non-blocking code.
The following code uses the supplyAsync method which internally gets thread from the common pool. Also, the supplyAsync returns CompletableFuture<String>
instance as the return type of supplier function is String
.
public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println(ForkJoinPool.getCommonPoolParallelism()); CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { System.out.println("SupplyAsync : " + Thread.currentThread().getName()); return "Success"; }); }
If you execute the above code, you can see in the console that the supplier code is executed by the worker thread of ForkJoinPool#commonPool
.
SupplyAsync : ForkJoinPool.commonPool-worker-3
Note that the supplier method returns a string as output. You can retrieve this result string by using the callbacks available with CompletableFuture.
For example, the whenComplete
method executes after the non-blocking supplier code completes its execution or throws any exception. You can retrieve the return value of the supplier method inside this function. Therefore, the below method prints the return value "Success"
.
cf.whenComplete((result, throwable) -> { if (throwable == null) { System.out.println("whenComplete : " + result + " in thread. " + Thread.currentThread().getName()); } }); /* prints whenComplete : Success in thread. ForkJoinPool.commonPool-worker-3 */
2.2. CompletableFuture supplyAsync with executor
The following supplyAsync
takes both the supplier function and executor. It uses the threads specified in the executor to run the supplier (non-blocking) code. This method returns CompletableFuture<U>
instance, meaning the supplier function returns value of type U.
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
In the below code, we are creating a fixed thread pool using the Executor
. The executor is passed as an argument to the supplyAsync
method. Also, the supplyAsync returns CompletableFuture<String>
instance as the return type of supplier function is String
.
ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> new Thread(r, "supplyAsycPool")); CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { System.out.println("supplyAsync executed in the thread of passed executor: " + Thread.currentThread().getName()); return "Success"; }, e);
If you run the above code, then you can notice that the non-blocking supplier code is executed by the thread created in the executor
.
supplyAsync executed in the thread of passed executor: supplyAsycPool
The supplyAsync method returns a string as output. You can retrieve this result string by using the callbacks available with CompletableFuture
.
For example, the whenComplete
method executes after the non-blocking supplier code completes its execution or throws any exception.
You can retrieve the return value of the supplier method inside this function. Therefore, the below method prints the return value "Success"
.
cf.whenComplete((result, throwable) -> { if (throwable == null) { System.out.println("whenComplete : " + result + " in thread. " + Thread.currentThread().getName()); } }); /* prints whenComplete : Success in thread. supplyAsycPool */
2.3. CompletableFuture runSync with common pool
The runSync accepts the runnable and uses the common thread pool (ForkJoinPool.commonPool
) to execute it. This method returns CompletableFuture<Void>
instance, meaning the supplier function does not return any value.
CompletableFuture<Void> runAsync(Runnable runnable)
For example, the following code uses the thread from the common pool.
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("runAsync executed in the thread: " + Thread.currentThread().getName()); });
If you execute the above code, then you can notice that the runAsync supplier code ran using the common pool thread.
runAsync executed in the thread: ForkJoinPool.commonPool-worker-3
2.4. CompletableFuture runSync with executor
The following runSync accepts both the runnable
and executor
. It uses the threads of the Executor to execute the non-blocking runnable code. This method returns CompletableFuture<Void>
instance, meaning the supplier function does not return any value.
CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
In the below code, we are creating a new thread pool using the Executors
and passing it to the runAsync
method.
ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> new Thread(r, "runAsyncThread")); CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("runAsync executed in the thread of passed executor: " + Thread.currentThread().getName()); }, e);
If you execute the above code, then you can notice that the runAsync supplier code ran using the created executor thread.
runAsync executed in the thread of passed executor: runAsyncThread
3. Conclusion
To sum up, we have learned the purpose of supplyAsync and runAsync methods available in the CompletableFuture along with examples. If you don’t want any output or result from the non-blocking code, then use runAsync
. Otherwise, use supplyAsync
method to execute your non-blocking code.
To learn more about Java, refer to our articles.
One thought on “CompletableFuture runAsync and supplyAsync”