Cả hai đều là Functional Interfaces đại diện cho task có thể chạy trên thread riêng, nhưng khác biệt về khả năng trả về kết quả, ném exception checked, và cách thực thi.
Bảng so sánh
| Tiêu chí | Runnable | Callable |
|---|---|---|
| Package | java.lang (Java 1.0) | java.util.concurrent (Java 5+) |
| Abstract method | void run() | V call() throws Exception |
| Return value | Không (void) | Có – kiểu generic V |
| Checked Exception | Không ném được (phải try-catch bên trong) | Có thể ném throws Exception |
| Thread creation | new Thread(runnable).start() | Không thể truyền trực tiếp cho Thread constructor |
| Executor submission | executor.execute(runnable) hoặc submit(runnable) | executor.submit(callable) → trả về Future |
| Bulk execution | Không hỗ trợ sẵn | invokeAll(Collection extends Callable>), invokeAny(...) |
| Functional Interface | Có (Java 8+) | Có (Java 8+) |
Ví dụ Runnable
public class RunnableDemo implements Runnable {
@Override
public void run() {
System.out.println("Hello from Runnable on " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Cách 1: Thread constructor
new Thread(new RunnableDemo()).start();
// Cách 2: Lambda + Thread
new Thread(() -> System.out.println("Lambda Runnable")).start();
// Cách 3: ExecutorService
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new RunnableDemo()); // void execute
es.submit(new RunnableDemo()); // Future> submit
es.shutdown();
}
}
Ví dụ Callable
import java.util.concurrent.*; public class CallableDemo implements Callable{ @Override public String call() throws Exception { Thread.sleep(1000); if (Math.random() > 0.5) throw new IOException("Random failure!"); return "Success from Callable"; } public static void main(String[] args) { ExecutorService es = Executors.newSingleThreadExecutor(); // Submit Callable → Future Future future = es.submit(new CallableDemo()); try { String result = future.get(); // Block, propagate exception System.out.println("Result: " + result); } catch (ExecutionException e) { System.err.println("Task failed: " + e.getCause().getMessage()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Bulk execution: invokeAll List > tasks = List.of( () -> 1, () -> 2, () -> { throw new RuntimeException("Fail"); } ); try { List > futures = es.invokeAll(tasks); for (Future f : futures) { try { System.out.println("InvokeAll: " + f.get()); } catch (ExecutionException ex) { System.out.println("Task failed: " + ex.getCause()); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } es.shutdown(); } }
Chuyển đổi Runnable ↔ Callable
// Runnable → Callable (với kết quả cố định) Callablecallable = Executors.callable(runnable, "result-value"); // Callable → Runnable (bỏ kết quả, handle exception) Runnable runnable = () -> { try { callable.call(); } catch (Exception e) { throw new RuntimeException(e); } }; // Hoặc dùng FutureTask (implements cả Runnable và Future) FutureTask futureTask = new FutureTask<>(callable); new Thread(futureTask).start(); // Chạy như Runnable String result = futureTask.get(); // Lấy kết quả như Future
Tóm tắt chọn lựa
- Runnable: Task đơn giản, không cần kết quả, không ném checked exception, tương thích Thread trực tiếp, legacy code.
- Callable: Task cần trả về kết quả, có thể ném checked exception, dùng với ExecutorService, Future, invokeAll/invokeAny, CompletableFuture.supplyAsync().
- Modern Java (CompletableFuture): Thường dùng
CompletableFuture.supplyAsync(() -> result)(dùng Supplier) thay vì Callable trực tiếp – API mạnh hơn (thenApply, thenCompose, exceptionally...).