8 Powerful Ways to Implement Asynchronous Programming in Java

This article explores eight Java asynchronous programming techniques—from raw threads and Future to CompletableFuture, Spring @Async, event-driven approaches, message queues, Hutool ThreadUtil, and Guava ListenableFuture—detailing their concepts, advantages, limitations, and providing complete code examples for each.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
8 Powerful Ways to Implement Asynchronous Programming in Java

Preface

Asynchronous execution is familiar to developers; it can greatly shorten request latency in scenarios such as sending SMS, emails, or updating data asynchronously.

Eight Ways to Implement Asynchrony

Thread

Future

CompletableFuture

Spring @Async annotation

Spring ApplicationEvent

Message queue

Third‑party async frameworks (e.g., Hutool ThreadUtil)

Guava async

What Is Asynchronous?

Consider a typical order‑placement flow where sending an SMS and awarding points are independent operations. In a synchronous model, the system must wait for the SMS to finish before proceeding to award points, causing unnecessary delay.

By executing these tasks asynchronously, both actions can run in parallel, reducing overall response time.

Asynchronous Programming

4.1 Thread Async

public class AsyncThread extends Thread {
    @Override
    public void run() {
        System.out.println("Current thread name:" + Thread.currentThread().getName() + " Send email success!");
    }
    public static void main(String[] args) {
        AsyncThread asyncThread = new AsyncThread();
        asyncThread.run();
    }
}

Creating a new Thread for each task is resource‑intensive; using a thread pool is preferred.

private ExecutorService executorService = Executors.newCachedThreadPool();
public void fun() {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            log.info("Execute business logic...");
        }
    });
}

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

4.2 Future Async

@Slf4j
public class FutureManager {
    public String execute() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("--- task start ---");
                Thread.sleep(3000);
                System.out.println("--- task finish ---");
                return "this is future execute final result!!!";
            }
        });
        String result = future.get(); // blocks if result not ready
        log.info("Future get result: {}", result);
        return result;
    }
    @SneakyThrows
    public static void main(String[] args) {
        new FutureManager().execute();
    }
}

Limitations of Future include lack of passive result notification, isolation of tasks, and poor error handling.

4.3 CompletableFuture Async

public class CompletableFutureCompose {
    @SneakyThrows
    public static void thenRunAsync() {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });
        CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something...");
        });
        System.out.println("cf1 result->" + cf1.get());
        System.out.println("cf2 result->" + cf2.get());
    }
    public static void main(String[] args) {
        thenRunAsync();
    }
}
CompletableFuture

uses an internal ForkJoinPool and supports task chaining without explicit thread‑pool management.

4.4 Spring @Async

Custom Async Thread Pool

@Configuration
@EnableAsync
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        int cores = Runtime.getRuntime().availableProcessors();
        System.out.println("System max threads: " + cores);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(16);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(99999);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("asyncServiceExecutor -");
        executor.setAwaitTerminationSeconds(60);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

Async Service Implementation

public interface AsyncService {
    MessageResult sendSms(String callPrefix, String mobile, String actionType, String content);
    MessageResult sendEmail(String email, String subject, String content);
}

@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
    @Autowired
    private IMessageHandler messageHandler;
    @Override
    @Async("taskExecutor")
    public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {
        try {
            Thread.sleep(1000);
            messageHandler.sendSms(callPrefix, mobile, actionType, content);
        } catch (Exception e) {
            log.error("SMS send exception -> ", e);
        }
        return null;
    }
    @Override
    @Async("taskExecutor")
    public MessageResult sendEmail(String email, String subject, String content) {
        try {
            Thread.sleep(1000);
            messageHandler.sendEmail(email, subject, content);
        } catch (Exception e) {
            log.error("Email send exception -> ", e);
        }
        return null;
    }
}

Using a custom thread pool with @Async is recommended over the default executor.

4.5 Spring ApplicationEvent Async

Define Event

public class AsyncSendEmailEvent extends ApplicationEvent {
    private String email;
    private String subject;
    private String content;
    private String targetUserId;
}

Event Handler

@Slf4j
@Component
public class AsyncSendEmailEventHandler implements ApplicationListener<AsyncSendEmailEvent> {
    @Autowired
    private IMessageHandler messageHandler;
    @Async("taskExecutor")
    @Override
    public void onApplicationEvent(AsyncSendEmailEvent event) {
        if (event == null) return;
        messageHandler.sendEmail(event.getEmail(), event.getSubject(), event.getContent());
    }
}

Combine with Spring Retry for fault tolerance when needed.

4.6 Message Queue Async

Producer

@Component
public class CallbackProducer {
    @Autowired
    private AmqpTemplate amqpTemplate;
    public void sendCallbackMessage(CallbackDTO dto, long delay) {
        log.info("Producer sending message: {}", dto);
        amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(),
            CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(),
            JsonMapper.getInstance().toJson(dto),
            message -> {
                message.getMessageProperties().setHeader("x-delay", delay);
                message.getMessageProperties().setCorrelationId(dto.getSdkId());
                return message;
            });
    }
}

Consumer

@Component
@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
public class CallbackConsumer {
    @Autowired
    private IGlobalUserService globalUserService;
    @RabbitHandler
    public void handle(String json, Channel channel, @Headers Map<String, Object> headers) throws Exception {
        if (headers.get("error") != null) {
            channel.basicNack((Long) headers.get(AmqpHeaders.DELIVERY_TAG), false, true);
            return;
        }
        try {
            CallbackDTO dto = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);
            globalUserService.execute(dto);
            channel.basicAck((Long) headers.get(AmqpHeaders.DELIVERY_TAG), false);
        } catch (Exception e) {
            log.error("Callback failed -> {}", e);
        }
    }
}

4.7 Hutool ThreadUtil

@Slf4j
public class ThreadUtils {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            ThreadUtil.execAsync(() -> {
                int number = ThreadLocalRandom.current().nextInt(20) + 1;
                System.out.println(number);
            });
            log.info("Current thread index: {}", i);
        }
        log.info("task finish!");
    }
}

4.8 Guava ListenableFuture

Guava’s ListenableFuture extends the JDK Future with a listener callback, eliminating the need for active polling.

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
ListenableFuture<Integer> listenableFuture = executorService.submit(() -> {
    log.info("callable execute...");
    TimeUnit.SECONDS.sleep(1);
    return 1;
});
Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {
    @Override
    public void onSuccess(Integer result) {
        System.out.println("Get listenable future's result with callback " + result);
    }
    @Override
    public void onFailure(Throwable t) {
        t.printStackTrace();
    }
});

These eight approaches cover low‑level thread control, high‑level abstractions, and integration with Spring and messaging systems, allowing developers to choose the most suitable async strategy for their Java backend applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaspringAsynchronousCompletableFutureThreadMessage QueueFuture
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

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.