Careeroza
Async Methods
Why Async
After a customer places an order, ShopEase sends an email, an SMS and updates analytics. The customer should not wait for all of that. @Async runs a method on another thread and returns immediately.
@SpringBootApplication
@EnableAsync
public class ShopEaseApplication { ... }
@Service
@Slf4j
public class NotificationService {
@Async
public void sendOrderEmail(String email, Long orderId) {
log.info("Sending email for order {}", orderId);
// call email provider (slow, ~800 ms)
}
@Async
public CompletableFuture<Boolean> sendSms(String phone, String text) {
// call SMS provider
return CompletableFuture.completedFuture(true);
}
}Example
The request thread and the async thread are different:
curl -X POST localhost:8080/api/orders ...Example output
INFO [nio-8080-exec-3] OrderController : Order 1001 placed, responding 201 INFO [ shopease-async-1] NotificationService : Sending email for order 1001
Rules for @Async
- Return
voidorCompletableFuture<T>. - The method must be
publicand called from another bean (proxy rule, see the AOP module). - Exceptions in
voidasync methods never reach the caller: configure anAsyncUncaughtExceptionHandler. - Spring Boot auto-configures a
ThreadPoolTaskExecutor(8 core threads), or a virtual-thread executor whenspring.threads.virtual.enabled=true.
What @Async Does Under the Hood
Example
An @Async call is just a task submitted to an executor. Here checkout fetches price, stock and coupon in parallel (200 ms each) and joins the results:
import java.util.concurrent.*;
public class Main {
static String slow(String what, String value) {
try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
return what + "=" + value;
}
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
long start = System.nanoTime();
CompletableFuture<String> price = CompletableFuture.supplyAsync(() -> slow("price", "54999"), pool);
CompletableFuture<String> stock = CompletableFuture.supplyAsync(() -> slow("stock", "12"), pool);
CompletableFuture<String> coupon = CompletableFuture.supplyAsync(() -> slow("coupon", "SAVE10"), pool);
CompletableFuture.allOf(price, stock, coupon).join();
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.println(price.get() + ", " + stock.get() + ", " + coupon.get());
System.out.println("Finished in under 500 ms (not 600): " + (ms < 500));
pool.shutdown();
}
}Output
price=54999, stock=12, coupon=SAVE10 Finished in under 500 ms (not 600): true