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