Callable và Future trong Java được sử dụng rất nhiều trong lập trình đa luồng. Trong những bài viết trước, chúng ta đã tìm hiểu nhiều về các luồng (thread) trong Java nhưng đôi khi chúng ta lại muốn có một luồng có thể trả về một giá trị nào đó để sử dụng. Từ Java 5, gói concurrency đã giới thiệu **interface java.util.concurrent.Callable trong gói concurrency, tương tự như interface Runnable, nhưng điểm khác biệt là nó có thể trả về một đối tượng bất kỳ và có thể ném ra Exception.

Java Callable là gì?
Interface Callable trong Java sử dụng Generic để định nghĩa kiểu dữ liệu trả về. Lớp Executors cung cấp các phương thức hữu ích để thực thi một Callable trong một thread pool (bộ quản lý luồng). Vì các tác vụ Callable chạy song song nên chúng ta cần chờ kết quả trả về từ các đối tượng này.
Java Future là gì?
Các tác vụ Callable trong Java trả về một đối tượng java.util.concurrent.Future. Thông qua đối tượng Future, chúng ta có thể kiểm tra trạng thái của tác vụ Callable và lấy về kết quả mà nó trả về.
Future cung cấp phương thức get() có thể đợi cho đến khi Callable hoàn tất rồi mới trả về kết quả. Ngoài ra, Future còn cung cấp phương thức cancel() để hủy bỏ tác vụ Callable đang được liên kết. Có một phiên bản nạp chồng (overloaded) của phương thức get(), cho phép chỉ định thời gian chờ kết quả, giúp tránh việc luồng hiện tại bị chặn (blocked) quá lâu. Ngoài ra còn có các phương thức isDone() và isCancelled() để kiểm tra trạng thái hiện tại của tác vụ Callable.
Dưới đây là một ví dụ đơn giản về tác vụ Callable trả về tên của luồng đang thực thi sau một giây. Chúng ta sử dụng Executor framework để thực thi 100 tác vụ song song và dùng Future để nhận kết quả từ các tác vụ đã được gửi đi.
Javapackage com.journaldev.threads;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
// trả về tên của thread đang thực thi tác vụ callable này
return Thread.currentThread().getName();
}
public static void main(String args[]) {
// Lấy ExecutorService từ lớp tiện ích Executors, với thread pool có kích thước là 10
ExecutorService executor = Executors.newFixedThreadPool(10);
// tạo một danh sách để giữ các đối tượng Future liên quan đến Callable
List<Future<String>> list = new ArrayList<Future<String>>();
// Tạo một instance của MyCallable
Callable<String> callable = new MyCallable();
for (int i = 0; i < 100; i++) {
// gửi các tác vụ Callable để được thực thi bởi thread pool
Future<String> future = executor.submit(callable);
// thêm Future vào danh sách, chúng ta có thể lấy giá trị trả về qua Future
list.add(future);
}
for (Future<String> fut : list) {
try {
// in ra giá trị trả về của Future, chú ý độ trễ của output trong console
// vì Future.get() sẽ đợi cho đến khi tác vụ hoàn thành
System.out.println(new Date() + "::" + fut.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// tắt executor service
executor.shutdown();
}
}
Sau khi chạy chương trình chạy, bạn sẽ thấy có độ trễ trong đầu ra vì phương thức get() của Future sẽ chờ cho tác vụ Callable hoàn tất. Đồng thời, chỉ có 10 luồng đang thực thi các tác vụ này. Đây là một đoạn output của chương trình trên:
Mon Dec 31 20:40:15 PST 2012::pool-1-thread-1
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-3
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-4
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-5
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-6
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-7
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-8
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-9
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-10
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
...
Mẹo: Vậy nếu chúng ta muốn ghi đè (override) một số phương thức của interface Future thì sao? Chẳng hạn như ghi đè phương thức get() để nó tự động hết hạn sau một khoảng thời gian mặc định thay vì chờ vô thời hạn thì chúng ta có thể sử dụng lớp FutureTask của Java vì đây là lớp triển khai cơ bản của interface Future.