Backend

99 Ngày Java — Ngày 65: Future và CompletableFuture

SSite Admin
30 tháng 09, 2026 2 phút đọc 0 lượt xem
99 Ngày Java — Ngày 65: Future và CompletableFuture

Ngày 64 đã gửi task vào ExecutorService. Future cho phép lấy kết quả sau, nhưng get() chặn luồng và khó ghép nhiều bước. CompletableFuture biến kết quả bất đồng bộ thành chuỗi xử lý và kết hợp các nhánh. Ta cần phân biệt ghép tuần tự với ghép song song, đồng thời đưa lỗi và timeout vào thiết kế.

Future và CompletableFuture

1. Callable và Future

Callable<T> trả kết quả hoặc ném lỗi; submit() trả Future<T>. get() đợi đến khi xong, có thể có timeout. Lỗi từ task được bọc trong ExecutionException; nguyên nhân thật là getCause(). Hủy bằng cancel(true) chỉ yêu cầu interrupt, task phải hợp tác. Không gọi get() trong từng vòng submit nếu mục đích là chạy đồng thời.

ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> answer = pool.submit(() -> 21 * 2);
try { System.out.println(answer.get(2, TimeUnit.SECONDS)); }
finally { pool.shutdown(); }

2. thenApply, thenCompose và thenCombine

thenApply biến giá trị T thành U. Nếu hàm trả một CompletionStage<U>, thenCompose làm phẳng chuỗi phụ thuộc, tránh CompletableFuture<CompletableFuture<U>>. thenCombine đợi hai nhánh độc lập rồi kết hợp kết quả. Tạo cả hai nhánh trước khi combine để chúng có cơ hội chạy đồng thời.

CompletableFuture<User> user = fetchUser(id);
CompletableFuture<Order> order = fetchOrder(id);
CompletableFuture<String> summary = user
    .thenCombine(order, (u, o) -> u.name() + ":" + o.total());
CompletableFuture<Profile> profile = user
    .thenCompose(u -> fetchProfile(u.profileId()));

3. Lỗi, timeout và luồng thực thi

exceptionally trả giá trị dự phòng khi stage trước lỗi; handle nhìn được cả giá trị và lỗi. orTimeout() hoàn tất stage bằng TimeoutException, còn completeOnTimeout() cung cấp fallback. join() ném CompletionException không checked; get() ném checked ExecutionException/InterruptedException. Callback không có hậu tố Async có thể chạy trên luồng hoàn thành stage; nhánh Async không truyền executor thường dùng common pool. Với blocking I/O, truyền executor do bạn quản lý và luôn đóng nó.

CompletableFuture<String> result = CompletableFuture
    .supplyAsync(() -> blockingLookup(id), pool)
    .orTimeout(2, TimeUnit.SECONDS)
    .exceptionally(error -> "fallback");
// Timeout does not guarantee the underlying blocking call stopped.

4. Bẫy khi ghép tác vụ

Đừng chặn bằng join() ngay trong một callback chạy trên pool nhỏ nếu callback đang đợi task cũng cần chính pool đó. Luôn quyết định chỗ nào được fallback, chỗ nào phải báo lỗi. allOf() chỉ báo các nhánh đã xong; muốn giá trị phải lấy từ từng future. Khi cần hủy cả luồng công việc, CompletableFuture không tự lan truyền cancel đến mọi I/O nền.

  • Bài tập: ghép hai API độc lập bằng thenCombine rồi đo thời gian so với gọi tuần tự.

  • Bài tập: thêm orTimeout và kiểm tra tài nguyên nền có thực sự dừng không.

Future là tay cầm cho một kết quả; CompletableFuture là đồ thị các bước phụ thuộc hoặc song song. Dùng timeout, xử lý lỗi và executor như một phần hợp đồng chứ không thêm sau cùng. Ngày 66 sẽ quay lại khóa và deadlock.

Tài liệu đối chiếu

  • https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Future.html

  • https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/CompletableFuture.html

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Spring — Ngày 66: Gọi API ngoài bằng RestClient và WebClient

Chọn client đồng bộ hoặc bất đồng bộ, đặt timeout, xử lý lỗi và retry có kiểm soát.

1 thg 10, 20262 phút2
99 Ngày Java — Ngày 66: Lock và deadlock

Dùng ReentrantLock, tryLock và thứ tự khóa cố định để tránh deadlock trên Java 21.

1 thg 10, 20262 phút1
99 Ngày Spring — Ngày 65: Xử lý bất đồng bộ với @Async

@EnableAsync, executor có giới hạn, CompletableFuture, lỗi của void task và giới hạn proxy/self-invocation.

30 thg 9, 20262 phút0