ExecutorService
is a part of the Java Concurrency framework that provides a higher-level mechanism for managing and controlling threads. It simplifies the process of managing thread execution, especially in multi-threaded applications. Here are some basic uses and key features of ExecutorService
:
1. Creating Thread Pools
ExecutorService
allows you to create a pool of threads, which can be reused for executing multiple tasks. This is more efficient than creating a new thread for each task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // Create a fixed thread pool with 2 threads ExecutorService executor = Executors.newFixedThreadPool(2); // Submit tasks to the executor executor.submit(() -> { System.out.println("Task 1 is running."); }); executor.submit(() -> { System.out.println("Task 2 is running."); }); // Shutdown the executor executor.shutdown(); } } |
2. Submitting Tasks
You can submit tasks to the ExecutorService
in various forms:
- Runnable: For tasks that do not return a result.
- Callable: For tasks that return a result or throw an exception.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); // Submit a Callable task Future<Integer> future = executor.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return 42; // Simulating a task that returns a result } }); try { Integer result = future.get(); // Get the result of the Callable System.out.println("Result from callable: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } |
3. Managing Task Execution
- Shutdown the Executor: You can gracefully shut down the executor using
shutdown()
orshutdownNow()
. - Await Termination: You can wait for all submitted tasks to finish using
awaitTermination()
.
1 2 3 4 5 6 7 8 |
executor.shutdown(); // Initiates an orderly shutdown try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); // Force shutdown if not terminated } } catch (InterruptedException e) { executor.shutdownNow(); // Force shutdown if interrupted } |
4. Handling Multiple Tasks
You can submit multiple tasks at once and manage their execution efficiently.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultiTaskExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { final int taskId = i; executor.submit(() -> { System.out.println("Task " + taskId + " is running."); }); } executor.shutdown(); } } |
5. Using invokeAll
and invokeAny
- invokeAll: Submits a collection of tasks and returns a list of
Future
objects representing the results. - invokeAny: Submits a collection of tasks and returns the result of the first successfully completed task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class InvokeExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); List<Callable<Integer>> tasks = Arrays.asList( () -> { Thread.sleep(1000); return 1; }, () -> { Thread.sleep(500); return 2; }, () -> { Thread.sleep(200); return 3; } ); try { // Invoke all tasks List<Future<Integer>> futures = executor.invokeAll(tasks); for (Future<Integer> future : futures) { System.out.println("Result: " + future.get()); } // Invoke any task Integer result = executor.invokeAny(tasks); System.out.println("First completed task result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } |
6. Scheduled Tasks
ScheduledExecutorService
extends ExecutorService
to allow you to schedule tasks to run after a delay or periodically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledTaskExample { public static void main(String[] args) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // Schedule a task to run after a delay scheduler.schedule(() -> System.out.println("Task executed after 2 seconds delay."), 2, TimeUnit.SECONDS); // Schedule a task to run periodically scheduler.scheduleAtFixedRate(() -> System.out.println("Periodic task executed."), 0, 5, TimeUnit.SECONDS); // Note: You might want to call shutdown() eventually to stop the scheduler. } } |
Summary
ExecutorService
is a powerful tool in Java that simplifies thread management and allows for efficient execution of tasks. By using thread pools, it helps reduce the overhead of creating and destroying threads. It provides various methods to submit tasks, manage their execution, and handle results, making it a fundamental component for concurrent programming in Java. If you have specific scenarios or additional questions about ExecutorService
, feel free to ask!