ExecutorService Interface
1 min
While Executor is a simple “fire and forget” interface, ExecutorService expands it into a full-featured management framework. It adds two critical capabilities: Lifecycle Management and Task Tracking.
Source Code #
Canonical Usage #
When to use: Use ExecutorService when you need to track the progress of a task, handle a return value (Future), or manage the orderly shutdown of your thread pool.
Common Patterns:
- Result-Bearing Tasks: Use
submit(Callable)to execute tasks that return a result or can throw checked exceptions. - Bulk Execution: Use
invokeAllto run multiple tasks and wait for all of them to complete (e.g., parallelizing a data processing job). - Lifecycle Management: Always ensure an
ExecutorServiceis shut down properly, often using a “two-phase” shutdown pattern (firstshutdown, thenshutdownNowif needed).
public <T> T runAndHandle(Callable<T> task) throws ExecutionException, InterruptedException {
Future<T> future = executorService.submit(task);
try {
return future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true); // Cancel if not done in time
throw e;
}
}
// Two-phase shutdown pattern
public void stop(ExecutorService pool) {
pool.shutdown();
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}