Backend Development 17 min read

Master Asynchronous Java: Threads, Futures, CompletableFuture & @Async

Explore the fundamentals and advanced techniques of asynchronous programming in Java, from basic thread creation and thread pools to Future, FutureTask, CompletableFuture, and Spring Boot’s @Async annotation, including practical code examples, event handling, and message queue integration for high‑throughput systems.

Sanyou's Java Diary
Sanyou's Java Diary
Sanyou's Java Diary
Master Asynchronous Java: Threads, Futures, CompletableFuture & @Async

Hello everyone, I'm Sanyou. Recently many friends asked for a summary of asynchronous programming, so let's discuss this topic.

Early systems were synchronous and easy to understand. When a user creates an e‑commerce order, the business logic flow can be long, increasing overall response time. Therefore, developers started extracting non‑core tasks from the main flow, giving rise to asynchronous programming.

Asynchronous programming is a way to let programs run concurrently. It allows multiple events to happen at the same time; when a long‑running method is called, it does not block the current execution flow, and the program can continue running.

The core idea is to use multithreading to turn serial operations into parallel ones, significantly reducing thread waiting time and improving system performance in high‑throughput scenarios.

1. Thread

The simplest way to create an asynchronous thread is to extend the Thread class.

<code>public class AsyncThread extends Thread{<br/>    @Override<br/>    public void run(){<br/>        System.out.println("Current thread name:" + this.getName() + ", executing thread name:" + Thread.currentThread().getName() + "-hello");<br/>    }<br/>}<br/></code>
<code>public static void main(String[] args){<br/>    // Simulate business flow<br/>    // ...<br/>    AsyncThread asyncThread = new AsyncThread();<br/>    asyncThread.start();<br/>}<br/></code>

Creating a new Thread for each task wastes resources, so a thread pool is preferred.

<code>@Bean(name = "executorService")<br/>public ExecutorService downloadExecutorService(){<br/>    return new ThreadPoolExecutor(20, 40, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000),<br/>        new ThreadFactoryBuilder().setNameFormat("defaultExecutorService-%d").build(),<br/>        (r, executor) -> log.error("defaultExecutor pool is full! "));<br/>}<br/></code>

Business logic can be wrapped in Runnable or Callable and submitted to the thread pool.

2. Future

When a task needs to return a result, Java 1.5 introduced Callable and Future .

<code>public interface Future<V>{<br/>    boolean cancel(boolean mayInterruptIfRunning);<br/>    boolean isCancelled();<br/>    boolean isDone();<br/>    V get() throws InterruptedException, ExecutionException;<br/>    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;<br/>}<br/></code>

Key methods:

cancel(): attempts to cancel the task.

isCancelled(): true if the task was cancelled before completion.

isDone(): true if the task has finished.

get(): blocks until the result is available.

get(long timeout, TimeUnit unit): returns null if the timeout expires.

Example:

<code>public class CallableAndFuture{<br/>    public static ExecutorService executorService = new ThreadPoolExecutor(4, 40,<br/>        0L, TimeUnit.MILLISECONDS,<br/>        new LinkedBlockingQueue<Runnable>(1024), new ThreadFactoryBuilder()<br/>            .setNameFormat("demo-pool-%d").build(), new ThreadPoolExecutor.AbortPolicy());<br/><br/>    static class MyCallable implements Callable<String>{<br/>        @Override<br/>        public String call() throws Exception{<br/>            return "Asynchronous processing, Callable returns result";<br/>        }<br/>    }<br/><br/>    public static void main(String[] args){<br/>        Future<String> future = executorService.submit(new MyCallable());<br/>        try{<br/>            System.out.println(future.get());<br/>        }catch(Exception e){<br/>            // handle<br/>        }finally{<br/>            executorService.shutdown();<br/>        }<br/>    }<br/>}<br/></code>

Future represents a possibly unfinished asynchronous task; calling get blocks until the result is ready.

3. FutureTask

FutureTask implements RunnableFuture , which extends both Runnable and Future . It can be submitted to a ThreadPoolExecutor or run directly by a thread, and it provides the task result.

Constructors:

<code>public FutureTask(Callable<V> callable)<br/>public FutureTask(Runnable runnable, V result)<br/></code>

Typical usage:

<code>ExecutorService executor = Executors.newCachedThreadPool();<br/>FutureTask<Integer> futureTask = new FutureTask<>(() -> {<br/>    System.out.println("Child thread starts calculating:");<br/>    int sum = 0;<br/>    for(int i = 1; i <= 100; i++) sum += i;<br/>    return sum;<br/>});<br/>executor.submit(futureTask);<br/>try{<br/>    System.out.println("Task result sum: " + futureTask.get());<br/>}catch(Exception e){<br/>    e.printStackTrace();<br/>}<br/>executor.shutdown();<br/></code>
Callable produces a result; Future retrieves the result.

4. CompletableFuture

JDK 1.8 introduced CompletableFuture , a non‑blocking asynchronous API based on functional programming. It allows callbacks for success or failure, avoiding the blocking get() of Future .

Example (tea‑making analogy):

<code>// Task 1: Wash kettle → Boil water<br>CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {<br/>    System.out.println("T1: Wash kettle...");<br/>    sleep(1, TimeUnit.SECONDS);<br/>    System.out.println("T1: Boil water...");<br/>    sleep(15, TimeUnit.SECONDS);<br/>});<br><br>// Task 2: Wash teapot → Wash cup → Get tea leaves<br>CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {<br/>    System.out.println("T2: Wash teapot...");<br/>    sleep(1, TimeUnit.SECONDS);<br/>    System.out.println("T2: Wash cup...");<br/>    sleep(2, TimeUnit.SECONDS);<br/>    System.out.println("T2: Get tea leaves...");<br/>    sleep(1, TimeUnit.SECONDS);<br/>    return "Longjing";<br/>});<br><br>// After both tasks finish, make tea<br>CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf) -> {<br/>    System.out.println("T1: Got tea leaves: " + tf);<br/>    System.out.println("T1: Brew tea...");<br/>    return "Serve tea: " + tf;<br/>});<br><br>// Wait for result<br>System.out.println(f3.join());<br></code>

CompletableFuture provides about 50 methods for serial, parallel, combination, and error handling.

5. SpringBoot @Async

Spring Boot offers annotation‑based asynchronous execution. Enable it with @EnableAsync , define a thread pool, and annotate methods with @Async .

<code>@SpringBootApplication<br/>@EnableAsync<br/>public class StartApplication{<br/>    public static void main(String[] args){<br/>        SpringApplication.run(StartApplication.class, args);<br/>    }<br/>}<br/></code>

Custom thread pool:

<code>@Configuration<br/>public class ThreadPoolConfiguration{<br/>    @Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")<br/>    public ThreadPoolExecutor systemCheckPoolExecutorService(){<br/>        return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,<br/>            new LinkedBlockingQueue<Runnable>(10000),<br/>            new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(),<br/>            (r, executor) -> log.error("system pool is full! "));<br/>    }<br/>}<br/></code>

Async method example:

<code>@Service<br/>public class AsyncServiceImpl implements AsyncService{<br/>    @Async("defaultThreadPoolExecutor")<br/>    public Boolean execute(Integer num){<br/>        System.out.println("Thread: " + Thread.currentThread().getName() + " , Task: " + num);<br/>        return true;<br/>    }<br/>}<br/></code>

Usage steps:

Add @EnableAsync to the application or configuration class.

Annotate target methods with @Async .

Ensure the class containing the async method is a Spring‑managed bean.

6. Spring ApplicationEvent

Spring’s event mechanism decouples components using ApplicationEvent and ApplicationListener , following the observer pattern.

<code>public class OrderEvent extends AbstractGenericEvent<OrderModel>{<br/>    public OrderEvent(OrderModel source){<br/>        super(source);<br/>    }<br/>}<br/></code>
<code>@Component<br/>public class OrderEventListener implements ApplicationListener<OrderEvent>{<br/>    @Override<br/>    public void onApplicationEvent(OrderEvent event){<br/>        System.out.println("[OrderEventListener] Handling event! " + JSON.toJSONString(event.getSource()));<br/>    }<br/>}<br/></code>
<code>OrderModel orderModel = new OrderModel();<br/>orderModel.setOrderId((long)i);<br/>orderModel.setBuyerName("Tom-" + i);<br/>orderModel.setSellerName("judy-" + i);<br/>orderModel.setAmount(100L);<br/>SpringUtils.getApplicationContext().publishEvent(new OrderEvent(orderModel));<br/></code>

By default, Spring events are synchronous. To make them asynchronous, configure a SimpleApplicationEventMulticaster with a TaskExecutor :

<code>@Component<br/>public class SpringConfiguration{<br/>    @Bean<br/>    public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("defaultThreadPoolExecutor") ThreadPoolExecutor defaultThreadPoolExecutor){<br/>        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();<br/>        multicaster.setTaskExecutor(defaultThreadPoolExecutor);<br/>        return multicaster;<br/>    }<br/>}<br/></code>

7. Message Queue

Message queues are a classic asynchronous architecture, offering high throughput and low latency. They consist of producers, the queue itself, and consumers, and support point‑to‑point or publish‑subscribe modes. Common implementations include RabbitMQ, Kafka, RocketMQ, ActiveMQ, and Pulsar.

JavaConcurrencyCompletableFuturethreadSpring BootAsynchronous ProgrammingFuture
Sanyou's Java Diary
Written by

Sanyou's Java Diary

Passionate about technology, though not great at solving problems; eager to share, never tire of learning!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.