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í | Executor | ExecutorService |
|---|---|---|
| Hierarchy | Parent interface | Extends Executor |
| Method chính | void execute(Runnable command) | Future> submit(Runnable task) |
| Return value | Không có (void) | Trả về Future – đại diện kết quả async |
| Task type | Chỉ Runnable | Runnable VÀ Callable |
| Lifecycle management | Không có | shutdown(), shutdownNow(), isShutdown(), isTerminated(), awaitTermination() |
| Batch submission | Không | invokeAll, invokeAny |
| Implementation chính | ThreadPoolExecutor, ScheduledThreadPoolExecutor, ForkJoinPool | Cù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 sangExecutorlàm mất khả năng shutdown!- Luôn dùng
ExecutorServicereference trong application code. Chỉ dùngExecutorkhi thiết kế API cho phép inject execution strategy. Future.get()block vô hạn. Dùngget(timeout, unit)để tránh deadlock.- Java 19+:
Virtual Threads(Project Loom) –Executors.newVirtualThreadPerTaskExecutor()trả về ExecutorService nhẹ, scalable.