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

Khác biệt giữa Executor và ExecutorService trong Java

Executor và ExecutorService là 2 interface cốt lõi của Executor Framework (Java 5+), trừu tượng hóa việc quản lý thread và thực thi task.

Mối quan hệ

  • Executor – Interface cha (parent), tối giản.
  • ExecutorService – Interface con, extends Executor, thêm lifecycle management và task submission với kết quả.

Bảng so sánh

Tiêu chíExecutorExecutorService
HierarchyParent interfaceExtends Executor
Method chínhvoid execute(Runnable command)Future submit(Runnable task)
Future submit(Callable task)
Future submit(Runnable task, T result)
Return valueKhông có (void)Trả về Future – đại diện kết quả async
Task typeChỉ RunnableRunnable VÀ Callable
Lifecycle managementKhông cóshutdown(), shutdownNow(), isShutdown(), isTerminated(), awaitTermination()
Batch submissionKhônginvokeAll, invokeAny
Implementation chínhThreadPoolExecutor, ScheduledThreadPoolExecutor, ForkJoinPoolCùng các implementation (vì implement ExecutorService)

Ví dụ Executor (Đơn giản, fire-and-forget)

import java.util.concurrent.*;

public class ExecutorDemo {
    public static void main(String[] args) {
        // Executor reference trỏ đến ThreadPoolExecutor implementation
        Executor executor = Executors.newSingleThreadExecutor();
        
        executor.execute(() -> {
            System.out.println("Task running on " + Thread.currentThread().getName());
            // Không thể lấy kết quả, không thể check exception
        });
        
        // Không có shutdown() trên Executor interface!
        // Cần cast hoặc dùng ExecutorService reference
        ((ExecutorService) executor).shutdown();
    }
}

Ví dụ ExecutorService (Đầy đủ tính năng)

import java.util.concurrent.*;

public class ExecutorServiceDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(2);
        
        // 1. Submit Runnable → Future
        Future future1 = service.submit(() -> {
            System.out.println("Runnable task done");
        });
        future1.get(); // Chờ hoàn thành (block), ném Exception nếu task throw
        
        // 2. Submit Callable → Future có kết quả
        Future future2 = service.submit(() -> {
            Thread.sleep(500);
            return "Result from Callable";
        });
        System.out.println("Got: " + future2.get()); // Block cho đến khi có kết quả
        
        // 3. Submit multiple, invokeAll
        List> tasks = List.of(
            () -> { Thread.sleep(100); return 1; },
            () -> { Thread.sleep(200); return 2; }
        );
        List> results = service.invokeAll(tasks);
        results.forEach(f -> {
            try { System.out.println("InvokeAll result: " + f.get()); } 
            catch (Exception e) { e.printStackTrace(); }
        });
        
        // 4. Lifecycle
        service.shutdown(); // Không nhận task mới, chờ task đang chạy xong
        service.awaitTermination(10, TimeUnit.SECONDS); // Block cho đến khi terminated
    }
}

Khi nào dùng cái nào?

  • Executor: API tối giản cho component chỉ cần "chạy task này đi" (fire-and-forget). Ví dụ: library code, framework internals, decouple task submission từ execution policy.
  • ExecutorService: Application code cần: lấy kết quả (Future), xử lý exception, shutdown gracefully, batch submit, timeout control.

Lưu ý quan trọng

  • Executors.newSingleThreadExecutor() trả về ExecutorService (wrapped). Ép kiểu sang Executor làm mất khả năng shutdown!
  • Luôn dùng ExecutorService reference trong application code. Chỉ dùng Executor khi thiết kế API cho phép inject execution strategy.
  • Future.get() block vô hạn. Dùng get(timeout, unit) để tránh deadlock.
  • Java 19+: Virtual Threads (Project Loom) – Executors.newVirtualThreadPerTaskExecutor() trả về ExecutorService nhẹ, scalable.