Trong lập trình đa luồng Java, Runnable và Callable là hai giao diện chính dùng để định nghĩa công việc thực thi trên các luồng. Tuy cùng mục đích, chúng có những khác biệt quan trọng về khả năng trả về giá trị và xử lý ngoại lệ.
Giao diện Runnable
Runnable là một functional interface với duy nhất phương thức run(), không trả về giá trị và không được phép ném ra checked exception.
public class Task implements Runnable {
@Override
public void run() {
System.out.println("Thực thi tác vụ trong luồng con");
}
}
Có thể khởi tạo và chạy tác vụ này qua đối tượng Thread:
public class App {
public static void main(String[] args) {
Thread worker = new Thread(new Task());
worker.start();
}
}
Từ Java 8 trở đi, nhờ đặc tính functional interface, Runnable có thể được biểu diễn gọn gàng bằng biểu thức lambda:
Thread t = new Thread(() -> {
System.out.println("Tác vụ đơn giản với lambda");
});
t.start();
Giao diện Callable
Khác với Runnable, Callable<V> cho phép trả về kết quả kiểu V và có thể ném ra checked exception. Phương thức duy nhất của nó là call().
import java.util.concurrent.*;
public class ResultTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// Mô phỏng xử lý tốn thời gian
Thread.sleep(1000);
return 42;
}
}
Để thực thi Callable, cần sử dụng ExecutorService. Kết quả được bao bọc trong đối tượng Future:
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(new ResultTask());
try {
Integer result = future.get(); // Chặn đến khi có kết quả
System.out.println("Kết quả: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
Dùng lambda cũng tương tự:
Callable<String> task = () -> {
if (Math.random() < 0.5) throw new Exception("Lỗi ngẫu nhiên");
return "Hoàn thành";
};
So sánh cốt lõi
- Giá trị trả về:
Runnable→void;Callable→ kiểu tham số hóaV. - Xử lý ngoại lệ:
run()không được khai báo ném checked exception;call()thì có. - Cơ chế thực thi:
Runnablecó thể chạy trực tiếp quaThread;Callablebắt buộc phải dùngExecutorService. - Kết quả bất đồng bộ: Chỉ
Callablehỗ trợ lấy kết quả thông quaFuture.