HelloKoding

Practical coding guides

CompletableFuture runAsync vs. supplyAsync in Java

Since Java 8, you can run a method asynchronously by using CompletableFuture.supplyAsync if you want to do something with the returning result or using CompletableFuture.runAsync if you don’t

Apart from that, you can also use lambda statements, lambda expressions, and method references to provide the parameters of runAsync and supplyAsync methods

Let’s walk through this tutorial to see the detail examples

runAsync has no return values while supplyAsync has

  • CompletableFuture.runAsync accepts a Runnable functional interface and return a CompletableFuture<Void> which doesn’t have value
@Test
public void runAsync() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .runAsync(() -> System.out.println("runAsync has no return values"));

    assertThat(c.get()).isNull();
}
  • CompletableFuture.supplyAsync accepts a Supplier<U> functional interface and return a CompletableFuture<U>
@Test
public void supplyAsync() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .supplyAsync(() -> "supplyAsync has return value");

    assertThat(c.get()).isEqualTo("supplyAsync has return value");
}

runAsync and supplyAsync callbacks chain

  • You can attach thenRun* to the runAsync callbacks chain
@Test
public void runAsyncWithCallbacks() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .runAsync(() -> System.out.println("runAsync"))
        .thenRunAsync(() -> System.out.println("callback"));

    assertThat(c.get()).isNull();
}
  • You can attach thenApply*, thenCombine*, thenCompose*, thenAccept* and thenRun* to the supplyAsync callbacks chain
@Test
public void supplyAsyncWithCallbacks() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .supplyAsync(() -> "supplyAsync")
        .thenApplyAsync((s) -> s + " callback");

    assertThat(c.get()).isEqualTo("supplyAsync callback");
}

Run runAsync and supplyAsync with an ExecutorService

Internally, CompletableFuture.runAsync(Runnable) and CompletableFuture.supplyAsync(Supplier) uses ForkJoinPool.commonPool() to execute

  • You can provision a custom Executor to CompletableFuture.runAsync(Runnable, Executor) as the following
@Test
public void runAsyncWithExecutor() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .runAsync(() -> System.out.println("Run runAsync with an Executor"), executorService);

    stop(executorService);

    assertThat(c.get()).isNull();
}
  • You can also provision a custom Executor to CompletableFuture.supplyAsync(Supplier, Executor)
@Test
public void supplyAsyncWithExecutor() throws ExecutionException, InterruptedException {
    CompletableFuture c = CompletableFuture
        .supplyAsync(() -> "run supplyAsync with an Executor", executorService);

    stop(executorService);

    assertThat(c.get()).isEqualTo("run supplyAsync with an Executor");
}

Conclusion

In this tutorial, we explored the difference, how to use CompletableFuture.runAsync and CompletableFuture.supplyAsync to run a Runnable or Supplier method asynchronously. You can find the full source code as below

RunAsyncVsSupplyAsyncTest.java

package com.hellokoding.java.concurrent;

import org.junit.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.hellokoding.java.concurrent.ConcurrentUtils.stop;
import static org.assertj.core.api.Assertions.assertThat;

public class RunAsyncVsSupplyAsyncTest {
    private ExecutorService executorService = Executors.newFixedThreadPool(2);

    @Test
    public void runAsync() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .runAsync(() -> System.out.println("runAsync has no return values"));

        assertThat(c.get()).isNull();
    }

    @Test
    public void runAsyncWithCallbacks() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .runAsync(() -> System.out.println("runAsync"))
            .thenRunAsync(() -> System.out.println("thenRunAsync callback"));

        assertThat(c.get()).isNull();
    }

    @Test
    public void runAsyncWithExecutor() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .runAsync(() -> System.out.println("Run runAsync with an Executor"), executorService);

        stop(executorService);

        assertThat(c.get()).isNull();
    }

    @Test
    public void supplyAsync() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .supplyAsync(() -> "supplyAsync has return value");

        assertThat(c.get()).isEqualTo("supplyAsync has return value");
    }

    @Test
    public void supplyAsyncWithCallbacks() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .supplyAsync(() -> "supplyAsync")
            .thenApplyAsync((s) -> s + " callback");

        assertThat(c.get()).isEqualTo("supplyAsync callback");
    }

    @Test
    public void supplyAsyncWithExecutor() throws ExecutionException, InterruptedException {
        CompletableFuture c = CompletableFuture
            .supplyAsync(() -> "run supplyAsync with an Executor", executorService);

        stop(executorService);

        assertThat(c.get()).isEqualTo("run supplyAsync with an Executor");
    }
}
Follow HelloKoding