Thread pools are one of the most common components in Java development and need to be closed when an application is shut down. But when closing the thread pool, use different methods for different situations. Here are some common ways to turn off thread pools.
This article is based on OpenJDK11
1. Introduction to thread pools
Thread pools in Java implement the Executor interface, which provides an entry point to perform tasks. ExecutorService is an Executor successor that provides management of the thread pool lifecycle, and is most commonly used in development.
There are three ways to turn off thread pools:
-
shutdown
-
shutdownNow
-
awaitTermination
Each of these approaches can turn off thread pools, but there are some differences in each approach.
The following tasks are available:
Class Task implements Runnable {@override public void run() {system.out.println (" Task implements "); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (" task completed "); }}Copy the code
After the shutdown method is called, the thread pool will not receive new tasks, and an error will be reported if the task is added forcibly. Tasks in the thread pool continue to execute, but are not guaranteed to complete, for reasons discussed below.
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.submit(new Task());
threadPool.shutdown();
threadPool.submit(new Task());
Copy the code
The result of the above code is as follows:
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@7e07db1f
Copy the code
After the shutdownNow method is called, all running tasks are terminated and the tasks waiting in the queue are returned.
ExecutorService threadPool = Executors.newFixedThreadPool(1);
threadPool.submit(new Task());
threadPool.shutdownNow();
Copy the code
After awaitTermination is called, tasks in the thread pool continue to run and can accept new tasks. It is thread blocking and does not terminate the main thread until the task completes, which is the difference from shutdown. You can set the timeout period, after which the thread will end blocking.
ExecutorService threadPool = Executors.newFixedThreadPool(1);
threadPool.submit(new Task());
threadPool.awaitTermination(6, TimeUnit.SECONDS);
Copy the code
2. Close the thread pool correctly
Perform simple tasks
Consider the Task above, which does not rely on external data and can be shutdown by calling the shutdown method on the thread pool executing the Task.
public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(2); threadPool.submit(new Task()); threadPool.submit(new Task()); threadPool.shutdown(); System.out.println(" main thread ends "); }Copy the code
The output of the program is as follows, and the task continues after the main thread ends.
Task Start Task Start Main thread End Task End Task End Task end Task endCopy the code
Perform complex tasks
When performing some complex tasks, tasks need to rely on external data. The following tasks are performed:
class Task1 implements Runnable { public Map<String, String> data; public Task1(Map<String, String> o) { this.data = o; } @override public void run() {while (data.containsKey("invoke")) {system.out.println (" normal task execute! ); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }} system.out.println (" Normal task ends "); }}Copy the code
Perform this task in the thread pool:
public static void main(String[] args) throws InterruptedException { ExecutorService threadPool = Executors.newFixedThreadPool(2); Map<String, String> data = new HashMap<>(); data.put("invoke", "true"); threadPool.submit(new Task1(data)); Thread.sleep(1000); threadPool.shutdown(); data.remove("invoke"); System.out.println(" main thread ends "); }Copy the code
The result of the program execution is as follows, and after the external data dependencies are removed, the program execution ends.
Normal task execution! The main thread ends. The normal task endsCopy the code
If the dependency is some network connection, or database connection, the connection will be destroyed after the main program exits, and the task in the thread pool cannot continue to execute. Block the main thread to give tasks in the thread pool some time to execute.
public static void main(String[] args) throws InterruptedException { ExecutorService threadPool = Executors.newFixedThreadPool(2); Map<String, String> data = new HashMap<>(); data.put("invoke", "true"); threadPool.submit(new Task1(data)); Thread.sleep(1000); threadPool.shutdown(); try { if (! ThreadPool. AwaitTermination (4, TimeUnit. SECONDS)) {System. Out. Println (" task isn't over "); } } catch (InterruptedException e) { e.printStackTrace(); } data.remove("invoke"); System.out.println(" main thread ends "); }Copy the code
After the awaitTermination method is called, the main thread will block, waiting for the task in the thread pool to continue executing, but it will not wait indefinitely. When the timeout expires, the main program will still exit.
When closing a thread pool, shutdown is typically used in conjunction with the awaitTermination method.
threadPool.shutdown(); if (! ThreadPool. AwaitTermination (5, TimeUnit. SECONDS)) {System. Out. Println (" task isn't over "); }Copy the code
If you also want to ensure that all tasks in the thread pool are finished when the thread pool exits, you can use shutdownNow after the blocking time has passed:
threadPool.shutdown(); if (! ThreadPool. AwaitTermination (5, TimeUnit. SECONDS)) {System. Out. Println (" task isn't over "); threadPool.shutdownNow(); }Copy the code
This allows the thread pool to close as expected.
The text/Rayjun