Backend Development 14 min read

Eight Ways to Implement Asynchronous Programming in Java

This article introduces eight practical approaches for achieving asynchronous execution in Java—including raw threads, Future, CompletableFuture, Spring @Async, ApplicationEvent, message queues, Hutool ThreadUtil, and Guava ListenableFuture—providing code samples, configuration tips, and discussion of each method's advantages and drawbacks.

Top Architect
Top Architect
Top Architect
Eight Ways to Implement Asynchronous Programming in Java

Asynchronous execution is essential for improving response time in scenarios such as sending SMS, emails, or updating data, where tasks can run independently of the main request flow.

1. Thread

Creating a custom thread subclass:

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

Using a thread pool is recommended to avoid the overhead of repeatedly creating threads:

private ExecutorService executorService = Executors.newCachedThreadPool();

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

2. Future

@Slf4j
public class FutureManager {
    public String execute() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future
future = executor.submit(new Callable
() {
            @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();
        log.info("Future get result: {}", result);
        return result;
    }

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

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

3. CompletableFuture

public class CompletableFutureCompose {
    @SneakyThrows
    public static void thenRunAsync() {
        CompletableFuture
cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });
        CompletableFuture
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();
    }
}

CompletableFuture uses ForkJoinPool internally and can be combined with custom executors.

4. Spring @Async

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

Service example using @Async with the custom pool:

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")
    @Override
    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("发送短信异常 -> ", e);
        }
    }

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

5. Spring ApplicationEvent

public class AsyncSendEmailEvent extends ApplicationEvent {
    private String email;
    private String subject;
    private String content;
    private String targetUserId;
}
@Component
public class AsyncSendEmailEventHandler implements ApplicationListener
{
    @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 error compensation if needed.

6. Message Queue (RabbitMQ)

@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),
                message -> {
                    message.getMessageProperties().setHeader("x-delay", delayTimes);
                    message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());
                    return message;
                });
    }
}
@Component
@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
public class CallbackConsumer {
    @Autowired
    private IGlobalUserService globalUserService;

    @RabbitHandler
    public void handle(String json, Channel channel, @Headers Map
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

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("当前第:" + i + "个线程");
        }
        log.info("task finish!");
    }
}

8. Guava ListenableFuture

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
ListenableFuture
listenableFuture = executorService.submit(() -> {
    log.info("callable execute...");
    TimeUnit.SECONDS.sleep(1);
    return 1;
});

Futures.addCallback(listenableFuture, new FutureCallback
() {
    @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 techniques cover low‑level thread management to high‑level framework utilities, allowing developers to choose the most suitable asynchronous strategy for their Java backend projects.

backendJavaSpringasynchronousCompletableFutureThreadFuture
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.