Khái Niệm Cốt Lõi
Khi làm việc với môi trường đa luồng trong Java, phương thức CompletableFuture.supplyAsync đóng vai trò quan trọng trong việc thực thi các tác vụ không chặn và trả về giá trị. Khác với runAsync chỉ thực hiện hành động mà không nhận kết quả, supplyAsync chấp nhận một hàm supplier để tính toán và trả về đối tượng CompletableFuture đại diện cho kết quả chưa hoàn thành.
Cấu Trúc Thực Thi Tiêu Chuẩn
Để tối ưu hóa tài nguyên hệ thống, thay vì sử dụng mặc định ForkJoinPool.commonPool(), nên khởi tạo riêng một bộ điều phối (Executor) phù hợp với quy mô ứng dụng. Dưới đây là ví dụ mô phỏng việc truy xuất dữ liệu từ cơ sở dữ liệu:
import java.util.concurrent.*;
public class AsyncDataFetcher {
private static final ExecutorService EXECUTOR =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static CompletableFuture<Integer> fetchDataAsync(int id) {
return CompletableFuture.supplyAsync(() -> {
try {
// Giả lập độ trễ khi truy vấn mạng
Thread.sleep(1500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CompletionException(e);
}
// Mô phỏng logic xử lý số học phức tạp
return performCalculation(id);
}, EXECUTOR);
}
private static int performCalculation(int input) {
return (int) Math.pow(input, 2) + 100;
}
public static void main(String[] args) throws InterruptedException {
// Gửi yêu cầu bất đồng bộ
CompletableFuture<Integer> futureResult = fetchDataAsync(5);
// Luồng chính tiếp tục thực hiện tác vụ khác
System.out.println("Đang xử lý luồng chính...");
// Lấy kết quả khi cần thiết
Integer finalValue = futureResult.join();
System.out.println("Kết quả tính toán: " + finalValue);
EXECUTOR.shutdown();
}
}
Xử Lý Ngoại Lệ Trong Dòng Chảy Asynchronous
Một lỗi phổ biến là bỏ qua việc bắt ngoại lệ. Nếu tác vụ nền gặp sự cố, việc gọi trực tiếp get() hoặc join() sẽ ném ra CompletionException. Để làm mượt dòng chảy, có thể áp dụng phương pháp hồi cứu lỗi:
- Sử dụng
exceptionallyđể cung cấp giá trị dự phòng khi xảy ra lỗi. - Sử dụng
handleđể xử lý cả trường hợp thành công lẫn thất bại cùng lúc.
// Cách tiếp cận an toàn hơn
CompletableFuture<Integer> safeResult = fetchDataAsync(0)
.exceptionally(throwable -> {
System.err.println("Lỗi phát sinh: " + throwable.getMessage());
return 0; // Giá trị mặc định
})
.thenApply(value -> value / 2);
Dây Chuyền Xử Lý Dữ Liệu
Sức mạnh của CompletableFuture nằm ở khả năng kết hợp nhiều bước xử lý theo trình tự hoặc song song. Phương thức thenApply cho phép biến đổi kết quả trước khi chuyển sang giai đoạn tiếp theo, trong khi thenCompose hữu ích khi các bước phụ thuộc vào kết quả trả về từ Future khác.
Đối với các tác vụ kết hợp độc lập, thenCombine giúp gộp hai kết quả từ hai luồng khác nhau vào một giá trị chung duy nhất mà không cần phải chờ thủ công từng cái một.