8 Powerful Ways to Implement Asynchronous Execution in Java

This article explains what asynchronous execution is, compares it with synchronous processing, and presents eight practical Java implementations—including raw threads, Future, CompletableFuture, Spring @Async, ApplicationEvent, message queues, Hutool ThreadUtil, and Guava ListenableFuture—complete with code samples and usage tips.

Programmer DD
Programmer DD
Programmer DD
8 Powerful Ways to Implement Asynchronous Execution in Java

Asynchronous execution is familiar to developers; it can greatly reduce request latency in scenarios such as sending SMS, emails, or performing asynchronous updates.

Eight Asynchronous Implementation Methods

Thread

Future

CompletableFuture

Spring @Async annotation

Spring ApplicationEvent

Message queue

Third‑party frameworks such as Hutool ThreadUtil

Guava asynchronous utilities

What Is Asynchrony?

Consider a typical order flow where sending an SMS and awarding points are independent; synchronous execution forces the second step to wait for the first, while asynchronous processing allows both to run concurrently.

By using asynchronous processing, the two operations can be performed simultaneously.

This demonstrates the simplicity of asynchrony.

1. Thread Asynchrony

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 wastes resources; a thread pool is preferred.

private ExecutorService executorService = Executors.newCachedThreadPool();

public void fun() {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            log.info("Executing business logic...");
        }
    });
}

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

2. Future Asynchrony

@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!!!";
            }
        });
        // get blocks until result is available
        String result = future.get();
        log.info("Future get result: {}", result);
        return result;
    }

    public static void main(String[] args) {
        FutureManager manager = new FutureManager();
        manager.execute();
    }
}

Output:

--- task start ---
--- task finish ---
Future get result: this is future execute final result!!!

Shortcomings of Future

Cannot receive task completion passively; must call get to obtain the result.

Futures are isolated; chaining requires manual handling, unlike CompletableFuture.

Lacks robust error handling; exceptions must be caught from get.

3. CompletableFuture Asynchrony

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 can be configured with a custom executor.

4. Spring @Async Asynchrony

Custom async thread pool configuration:

@EnableAsync
@Configuration
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("System max threads: " + processors);
        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;
    }
}

Service interface and implementation using @Async:

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

@Service
public class AsyncServiceImpl implements AsyncService {
    @Autowired
    private IMessageHandler messageHandler;

    @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;
    }

    @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 implementation.

5. Spring ApplicationEvent Asynchrony

Define an event class:

public class AsyncSendEmailEvent extends ApplicationEvent {
    private String email;
    private String subject;
    private String content;
    private String targetUserId;
    // getters and setters omitted
}

Event handler:

@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(), event.getTargetUserId());
    }
}

Combine with Spring Retry for compensation when errors occur.

6. Message Queue Asynchrony

Producer example:

@Component
public class CallbackProducer {
    @Autowired
    AmqpTemplate amqpTemplate;

    public void sendCallbackMessage(CallbackDTO callbackDTO, long delayTimes) {
        log.info("Producer sending message, callbackDTO, {}", callbackDTO);
        amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(),
            CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(),
            JsonMapper.getInstance().toJson(callbackDTO),
            message -> {
                message.getMessageProperties().setHeader("x-delay", delayTimes);
                message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());
                return message;
            });
    }
}

Consumer example:

@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 callbackDTO = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);
            globalUserService.execute(callbackDTO);
            channel.basicAck((Long) headers.get(AmqpHeaders.DELIVERY_TAG), false);
        } catch (Exception e) {
            log.error("Callback failure -> {}", e);
        }
    }
}

7. Hutool ThreadUtil Asynchrony

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

8. Guava Asynchrony

Guava's ListenableFuture extends JDK Future with a listener mechanism.

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
final ListenableFuture<Integer> listenableFuture = executorService.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        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();
    }
});
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.

springAsynchronousCompletableFutureThreadFuture
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.