Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Java

Khác biệt giữa Runnable và Callable Interface trong Java

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íRunnableCallable
Packagejava.lang (Java 1.0)java.util.concurrent (Java 5+)
Abstract methodvoid run()V call() throws Exception
Return valueKhông (void)Có – kiểu generic V
Checked ExceptionKhông ném được (phải try-catch bên trong)Có thể ném throws Exception
Thread creationnew Thread(runnable).start()Không thể truyền trực tiếp cho Thread constructor
Executor submissionexecutor.execute(runnable) hoặc submit(runnable)executor.submit(callable) → trả về Future
Bulk executionKhông hỗ trợ sẵninvokeAll(Collection), invokeAny(...)
Functional InterfaceCó (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)
Callable callable = 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...).