Eight Ways to Implement Asynchronous Execution in Java

This article introduces eight practical techniques for achieving asynchronous processing in Java—including raw threads, Future, CompletableFuture, Spring @Async, ApplicationEvent, message queues, Hutool ThreadUtil, and Guava ListenableFuture—complete with code examples and discussion of their advantages and limitations.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Eight Ways to Implement Asynchronous Execution in Java

Asynchronous execution is a common requirement in modern development to reduce latency for tasks such as sending SMS, emails, or updating data, allowing independent operations to run concurrently.

Eight implementation approaches are presented:

Thread

Future

CompletableFuture

Spring @Async annotation

Spring ApplicationEvent

Message queue

Third‑party utilities like Hutool ThreadUtil

Guava ListenableFuture

1. Thread

Creating a custom thread class and invoking run() directly:

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();
    }
}

Because repeatedly creating raw Thread objects is costly, a thread pool is recommended:

private ExecutorService executorService = Executors.newCachedThreadPool();

public void fun() {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            log.info("执行业务逻辑...");
        }
    });
}

2. Future

Submitting a Callable to an ExecutorService returns a Future whose result can be obtained with get() (blocking):

@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!!!";
            }
        });
        // Blocking call
        String result = future.get();
        log.info("Future get result: {}", result);
        return result;
    }
    public static void main(String[] args) {
        FutureManager manager = new FutureManager();
        manager.execute();
    }
}

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

3. CompletableFuture

Using the JDK's CompletableFuture enables non‑blocking composition without explicitly managing an executor (it uses ForkJoinPool by default):

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结果->" + cf1.get());
        System.out.println("cf2结果->" + cf2.get());
    }
    public static void main(String[] args) {
        thenRunAsync();
    }
}

4. Spring @Async

Define a custom thread‑pool bean and annotate service methods with @Async("taskExecutor"):

@EnableAsync
@Configuration
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        int i = Runtime.getRuntime().availableProcessors();
        System.out.println("系统最大线程数  : " + i);
        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;
    }
}
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 mesageHandler;

    @Override
    @Async("taskExecutor")
    public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {
        try {
            Thread.sleep(1000);
            mesageHandler.sendSms(callPrefix, mobile, actionType, content);
        } catch (Exception e) {
            log.error("发送短信异常 -> ", e);
        }
    }

    @Override
    @Async("taskExecutor")
    public MessageResult sendEmail(String email, String subject, String content) {
        try {
            Thread.sleep(1000);
            mesageHandler.sendsendEmail(email, subject, content);
        } catch (Exception e) {
            log.error("发送email异常 -> ", e);
        }
    }
}

5. Spring ApplicationEvent

Define a custom event class and an ApplicationListener that processes the event asynchronously:

public class AsyncSendEmailEvent extends ApplicationEvent {
    private String email;
    private String subject;
    private String content;
    private String targetUserId;
}
@Slf4j
@Component
public class AsyncSendEmailEventHandler implements ApplicationListener<AsyncSendEmailEvent> {
    @Autowired
    private IMessageHandler mesageHandler;

    @Async("taskExecutor")
    @Override
    public void onApplicationEvent(AsyncSendEmailEvent event) {
        if (event == null) return;
        String email = event.getEmail();
        String subject = event.getSubject();
        String content = event.getContent();
        String targetUserId = event.getTargetUserId();
        mesageHandler.sendsendEmailSms(email, subject, content, targetUserId);
    }
}

When combined with Spring Retry, this approach can provide compensation for failures.

6. Message Queue

Using RabbitMQ, a producer sends a delayed callback message and a consumer processes it with manual acknowledgment:

@Slf4j
@Component
public class CallbackProducer {
    @Autowired
    AmqpTemplate amqpTemplate;

    public void sendCallbackMessage(CallbackDTO callbackDTO, final long delayTimes) {
        log.info("生产者发送消息,callbackDTO,{}", callbackDTO);
        amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(),
                CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(),
                JsonMapper.getInstance().toJson(callbackDTO),
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        message.getMessageProperties().setHeader("x-delay", delayTimes);
                        message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());
                        return message;
                    }
                });
    }
}
@Slf4j
@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> map) throws Exception {
        if (map.get("error") != null) {
            channel.basicNack((Long) map.get(AmqpHeaders.DELIVERY_TAG), false, true);
            return;
        }
        try {
            CallbackDTO callbackDTO = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);
            globalUserService.execute(callbackDTO);
            channel.basicAck((Long) map.get(AmqpHeaders.DELIVERY_TAG), false);
        } catch (Exception e) {
            log.error("回调失败 -> {}", e);
        }
    }
}

7. Hutool ThreadUtil

Hutool provides a simple async execution utility:

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

8. Guava ListenableFuture

Guava extends Future with ListenableFuture, allowing callbacks without blocking:

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();
    }
});

The article concludes with a reminder to like, share, and follow the author’s public account, and promotes additional paid resources such as Spring full‑stack series, massive data sharding, DDD micro‑service tutorials, and a knowledge‑sharing community.

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.

JavaspringAsynchronousThreadMessage QueueGuavaFuture
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.