package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Service
public class AsyncService {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
// Simple async operation
public CompletableFuture<String> fetchUserData(Long userId) {
return CompletableFuture.supplyAsync(() -> {
// Simulate API call
sleep(1000);
return "User data for ID: " + userId;
}, executor);
}
// Chain operations
public CompletableFuture<String> processUserData(Long userId) {
return fetchUserData(userId)
.thenApply(data -> data.toUpperCase())
.thenApply(data -> "Processed: " + data);
}
// Combine multiple futures
public CompletableFuture<String> getUserProfile(Long userId) {
CompletableFuture<String> userFuture = fetchUserData(userId);
CompletableFuture<String> postsFuture = fetchUserPosts(userId);
CompletableFuture<String> friendsFuture = fetchUserFriends(userId);
return userFuture.thenCombine(postsFuture, (user, posts) ->
user + ", " + posts)
.thenCombine(friendsFuture, (combined, friends) ->
combined + ", " + friends);
}
// Sequential composition
public CompletableFuture<String> getRecommendations(Long userId) {
return fetchUserData(userId)
.thenCompose(userData -> {
// Use userData to fetch recommendations
return CompletableFuture.supplyAsync(() -> {
sleep(500);
return "Recommendations based on: " + userData;
}, executor);
});
}
// Exception handling
public CompletableFuture<String> fetchWithFallback(Long userId) {
return fetchUserData(userId)
.exceptionally(ex -> {
System.err.println("Error fetching user: " + ex.getMessage());
return "Default user data";
});
}
// Handle both success and failure
public CompletableFuture<String> fetchWithHandling(Long userId) {
return fetchUserData(userId)
.handle((result, ex) -> {
if (ex != null) {
return "Error: " + ex.getMessage();
}
return result;
});
}
// Wait for all futures
public CompletableFuture<List<String>> fetchMultipleUsers(List<Long> userIds) {
List<CompletableFuture<String>> futures = userIds.stream()
.map(this::fetchUserData)
.collect(Collectors.toList());
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
// Race multiple operations
public CompletableFuture<String> fetchFromFastestSource(Long userId) {
CompletableFuture<String> db = fetchFromDatabase(userId);
CompletableFuture<String> cache = fetchFromCache(userId);
CompletableFuture<String> api = fetchFromAPI(userId);
return CompletableFuture.anyOf(db, cache, api)
.thenApply(result -> (String) result);
}
// Spring @Async annotation alternative
@Async
public CompletableFuture<String> asyncMethod(Long userId) {
sleep(2000);
return CompletableFuture.completedFuture("Async result for " + userId);
}
// Helper methods
private CompletableFuture<String> fetchUserPosts(Long userId) {
return CompletableFuture.supplyAsync(() -> {
sleep(800);
return "Posts for user " + userId;
}, executor);
}
private CompletableFuture<String> fetchUserFriends(Long userId) {
return CompletableFuture.supplyAsync(() -> {
sleep(600);
return "Friends of user " + userId;
}, executor);
}
private CompletableFuture<String> fetchFromDatabase(Long userId) {
return CompletableFuture.supplyAsync(() -> {
sleep(1500);
return "DB: User " + userId;
}, executor);
}
private CompletableFuture<String> fetchFromCache(Long userId) {
return CompletableFuture.supplyAsync(() -> {
sleep(100);
return "Cache: User " + userId;
}, executor);
}
private CompletableFuture<String> fetchFromAPI(Long userId) {
return CompletableFuture.supplyAsync(() -> {
sleep(2000);
return "API: User " + userId;
}, executor);
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
CompletableFuture enables non-blocking asynchronous programming in Java. I use supplyAsync() to run tasks in thread pools and thenApply() to chain transformations. thenCompose() flattens nested futures, while thenCombine() merges independent futures. Exception handling uses exceptionally() or handle() to recover from failures. allOf() waits for multiple futures, anyOf() completes when any finishes. CompletableFuture integrates with Spring's @Async for method-level parallelism. Callbacks execute on completion without blocking threads. Custom executors control thread pool behavior. The API supports both callback and imperative styles with join() or get(). CompletableFuture improves throughput by freeing threads during I/O operations, essential for scalable microservices.