8 Powerful Ways to Implement Asynchronous Processing in Java
This article explains why asynchronous execution is essential for reducing latency in Java applications and walks through eight practical implementations—including threads, Future, CompletableFuture, Spring @Async, ApplicationEvent, message queues, ThreadUtil, and Guava—complete with code examples and usage tips.
Asynchronous execution is familiar to developers; it can dramatically shorten request latency in scenarios such as sending SMS, emails, or updating data.
Eight ways to implement async
Thread
Future
CompletableFuture
Spring @Async
Spring ApplicationEvent
Message Queue
ThreadUtil async utility
Guava async
What is async?
Consider a typical order‑placement flow where sending an SMS and awarding points are independent. In a synchronous flow the SMS call blocks until the points are awarded, increasing latency.
By using async, both operations can run concurrently, eliminating the wait.
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 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("执行业务逻辑...");
}
});
}Business logic can be wrapped in Runnable or Callable and submitted to the pool.
2. Future async
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!!!";
}
});
// This blocks when a return value is needed
String result = future.get();
log.info("Future get result: {}", result);
return result;
}
public static void main(String[] args) {
FutureManager manager = new FutureManager();
manager.execute();
}
} --- task start ---
--- task finish ---
Future get result: this is future execute final result!!!Future shortcomings
Result must be actively retrieved with get(); no passive notification.
Futures are isolated; they cannot be chained into pipelines.
Error handling is poor; exceptions are only visible when get() is called.
3. CompletableFuture
public class CompletableFutureCompose {
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();
}
}CompletableFuture uses an internal ForkJoinPool; a custom pool can be supplied if needed.
4. Spring @Async
Define a custom async thread pool:
@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;
}
}Async service interface and 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
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);
}
return null;
}
@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);
}
return null;
}
}It is recommended to use a custom thread pool rather than the default @Async implementation.
5. Spring ApplicationEvent
Define an async event:
public class AsyncSendEmailEvent extends ApplicationEvent {
private String email;
private String subject;
private String content;
private String targetUserId;
// getters and setters omitted
}Define the event handler:
@Slf4j
@Component
public class AsyncSendEmailEventHandler implements ApplicationListener<AsyncSendEmailEvent> {
@Autowired
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, it can provide compensation for failures.
6. Message Queue
Callback producer:
@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;
}
});
}
}Callback consumer:
@Slf4j
@Component
@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
public class CallbackConsumer {
@Autowired
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. ThreadUtil async utility
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 async
Guava’s ListenableFuture extends Future with an addListener method, 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();
}
});These eight approaches cover the most common ways to achieve asynchronous processing in Java.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
