CompletableFuture cơ bản
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Gọi API thanh toán, đọc database, tải file: những việc này mất từ vài chục
mili giây tới vài giây. Bài này hướng dẫn cách chạy việc chậm mà không chặn
luồng đang chạy, bằng CompletableFuture.
Khái niệm
📨 CompletableFuture: object đại diện cho một việc đang chạy và sẽ xong sau.
CompletableFuture<T> là việc sẽ trả về một giá trị kiểu T.
⏳ supplyAsync/join: supplyAsync chạy một lambda ở luồng khác và trả về ngay một CompletableFuture, join đứng chờ việc đó xong rồi lấy kết quả.
Ví dụ
// Main.java
void main() {
long price = getPriceAsync("PEN-01").join();
System.out.println(price); // 5000
}
CompletableFuture<Long> getPriceAsync(String code) {
// chờ 1 giây rồi mới chạy: giả lập gọi mạng
Executor later = CompletableFuture.delayedExecutor(
1, TimeUnit.SECONDS);
return CompletableFuture.supplyAsync(
() -> 5000L, later);
}- Kiểu trả về là
CompletableFuture<Long>chứ không phảilong. Lúc method trả về, việc tra giá chưa xong. supplyAsync(() -> 5000L, later)chạy lambda ở luồng khác.laterchờ 1 giây rồi mới chạy lambda, dùng để giả lập việc chậm.- Tên method kết thúc bằng
Asyncđể nơi gọi biết kết quả chưa có ngay, như chínhsupplyAsync. join()lấy ra giá trịLongtừCompletableFuture<Long>.- Muốn xử lý tiếp kết quả mà không đứng chờ, dùng
thenApply(p -> ...): lambda chạy khi có kết quả.
Thử ngay
Dán vào Main.java rồi chạy java Main.java:
// Main.java
void main() {
System.out.println("1. Bắt đầu");
CompletableFuture<Long> task =
getPriceAsync("PEN-01");
System.out.println("2. Đã gọi, chưa chờ");
System.out.println("3. Giá: " + task.join());
}
CompletableFuture<Long> getPriceAsync(String code) {
System.out.println(" đang tra giá...");
Executor later = CompletableFuture.delayedExecutor(
500, TimeUnit.MILLISECONDS);
return CompletableFuture.supplyAsync(() -> {
System.out.println(" tra xong");
return 5000L;
}, later);
}Đoán trước khi chạy: dòng "2. Đã gọi, chưa chờ" in trước hay sau "tra xong"?
Xem kết quả
1. Bắt đầu
đang tra giá...
2. Đã gọi, chưa chờ
tra xong
3. Giá: 5000In trước. Gọi getPriceAsync thì dòng "đang tra giá..." in ngay, rồi
supplyAsync trả về nơi gọi mà không đợi việc tra giá.
Chương trình in dòng 2 trong lúc việc tra giá vẫn đang chờ. Chỉ tới
task.join() mới thật sự đứng đợi kết quả.
Lỗi hay gặp
Quên join. Không có join thì bạn nhận về CompletableFuture, không
phải giá trị.
// SAI — lỗi compile: thiếu join()
long price = getPriceAsync("PEN-01");// ĐÚNG
long price = getPriceAsync("PEN-01").join();Gọi join ngay để xử lý tiếp. join chặn luồng hiện tại cho tới khi có
kết quả. Trong ứng dụng web, việc này làm server chậm đi, có khi treo hẳn.
// SAI — chặn luồng trong lúc chờ giá
long price = getPriceAsync("PEN-01").join();
long withVat = price * 110 / 100;// ĐÚNG — thenApply không chặn luồng
CompletableFuture<Long> withVat =
getPriceAsync("PEN-01")
.thenApply(p -> p * 110 / 100);
System.out.println(withVat.join()); // 5500Tóm tắt
CompletableFuture<T>là việc sẽ trả vềTkhi xong.supplyAsyncchạy lambda ở luồng khác và trả về ngay,joinchờ rồi lấy kết quả.thenApplyxử lý tiếp kết quả khi có mà không chặn luồng.- Chỉ
joinở nơi thật sự cần kết quả, như cuốimain.
Tự kiểm tra
0/3 câuMethod chạy việc chậm ở luồng khác nhưng không trả về giá trị nào nên khai báo kiểu trả về là gì?
CompletableFuture<String> getNameAsync() trả về "An". Dòng nào lấy đúng chuỗi "An"?
Việc nào nên chạy bằng CompletableFuture?