Core Values
In the field of concurrent programming, Java's ExecutorService (located in packages) is a key interface for thread pool management. As the core component of the Executor framework, it provides developers with:
- Thread lifecycle management automation
- Intelligent scheduling of task queues
- Resource reuse optimization mechanism
- Asynchronous execution result tracking capability
2. Necessity of shutdown mechanism
Incorrect thread pool shutdown will cause:
- Memory leak (stuck thread cannot be recycled)
- The application cannot be terminated normally (non-daemon threads remain active)
- Inconsistent task status (sudden interruption causes data problems)
- System resource exhaustion (unlimited thread creation)
() Method Detailed Explanation
3.1 Method Features
void shutdown()
State transition
Set the thread pool status to SHUTDOWN triggers the following behavior:
- Rejected new task submission (triggered RejectedExecutionHandler)
- Continue to perform existing tasks:
- Running tasks
- queued tasks
Typical application scenarios
ExecutorService executor = (4); // Submit multiple tasks...(); try { if (!(60, )) { ("There are still tasks that have not been completed within the time limit"); } } catch (InterruptedException e) { ().interrupt(); }
3.2 Internal operating mechanism
- Atomic state update: CAS operation modifies thread pool control status
- Interrupt idle thread: only interrupt the Worker thread waiting for tasks
- Queue Consumption Guarantee: Completely handle remaining tasks in BlockingQueue
() Method analysis
4.1 Method definition
List<Runnable> shutdownNow()
State transition
Set the thread pool status to STOP, triggering:
- Reject new tasks immediately
- Interrupt all worker threads (whether or not)
- Clear the task queue and return to the list of unexecuted tasks
4.2 Key points for interrupt processing
(); // Typical return value processingList<Runnable> unprocessed = (); if (!()) { ("throw away{}Unexecuted tasks", ()); }
Task interrupt conditions
It can only be terminated if the task code correctly handles interrupts:
class InterruptibleTask implements Runnable { public void run() { while (!().isInterrupted()) { // Perform interruptible operations try { (1000); } catch (InterruptedException e) { ().interrupt(); // Reset interrupt status break; } } } }
5. Comparative analysis
characteristic | shutdown() | shutdownNow() |
---|---|---|
New mission acceptance | Reject immediately | Reject immediately |
Running task processing | Wait for completion | Try to interrupt |
Queue task processing | Execute all | Clear and return |
Return value | void | Task list not executed |
Applicable scenarios | Elegantly close | Emergency termination |
Thread interrupt policy | Only interrupt idle threads | Force interruption of all threads |
6. Best Practice Code Examples
6.1 Standard Close Template
public class GracefulShutdownExample { // Define timeout and time units (30 seconds) private static final int TIMEOUT = 30; private static final TimeUnit UNIT = ; // Method to execute tasks, receive a task list and submit it to the thread pool for execution public void executeTasks(List<Runnable> tasks) { // Create a fixed-size thread pool with the size of the number of processor cores available in the system ExecutorService executor = (().availableProcessors()); try { // Submit each task in the task list to the thread pool (executor::submit); } finally { // After all tasks are submitted, disable the thread pool to receive new tasks and start elegantly closing the thread pool (); // No new tasks are submitted try { // Wait for the task in the thread pool to complete within the specified timeout. If the timeout does not complete, force the thread pool to be closed if (!(TIMEOUT, UNIT)) { // If it fails to complete within the timeout, call shutdownNow() to force terminate all active tasks List<Runnable> unfinished = (); // Handle unfinished tasks such as logging or resubmitting handleUnfinishedTasks(unfinished); } } catch (InterruptedException e) { // If it is interrupted while waiting for termination, restore the interrupt state and force the thread pool to be closed ().interrupt(); (); } } } // Methods to deal with unfinished tasks, here we print the number of unfinished tasks private void handleUnfinishedTasks(List<Runnable> tasks) { // If there are unfinished tasks, print the number of tasks and perform additional processing if (!()) { ("Number of tasks not completed: " + ()); // You can record logs here, requeuing unfinished tasks, etc. } } }
Construct thread pool: ()
Create a fixed-size thread pool that is the number of processor cores available to the system, which can make it more efficient to utilize CPU resources.
Submit task:use(executor::submit)
Submit each task to the thread pool for execution.
Elegantly close thread pool:
-
()
Disable the thread pool to receive new tasks, but still executes the submitted tasks. -
awaitTermination()
Methods are used to wait for all tasks to complete. If the task does not complete after the timeout, callshutdownNow()
Force the thread pool to close, stop all running tasks, and return unfinished tasks.
Handling interrupts:If it occurs while waiting for terminationInterruptedException
, the thread will restore the interrupt state and force the thread pool to be closed.
Handling unfinished tasks: handleUnfinishedTasks()
Methods will handle unfinished tasks, such as logging or requeuing unfinished tasks.
6.2 Enhanced implementation of callbacks
public class EnhancedExecutorManager { // Define thread pool object private final ExecutorService executor; // Define timeout and unit private final long timeout; private final TimeUnit unit; // Constructor, initialize the thread pool and set the timeout and unit public EnhancedExecutorManager(int corePoolSize, long timeout, TimeUnit unit) { // Create a thread pool with corePoolSize and maximum pool size corePoolSize * 2, maximum idle time 60 seconds = new ThreadPoolExecutor( corePoolSize, // Core thread pool size corePoolSize * 2, // Maximum thread pool size 60L, , // Living time of idle thread new LinkedBlockingQueue<>(1000), // Use a queue with capacity of 1000 to cache tasks new CustomThreadFactory(), // Custom thread factory new () // When the task cannot be submitted, the caller thread executes the task ); = timeout; // Set the timeout time = unit; // Set timeout unit } // How to close thread pool elegantly public void shutdown() { (); // First try to close the thread pool normally and no longer receive new tasks try { // Forced shutdown if the thread pool fails to terminate within the specified timeout if (!(timeout, unit)) { ("Forced termination of thread pool..."); // Force stop all executing tasks and return to the discarded task list List<Runnable> droppedTasks = (); ("Number of discarded tasks: " + ()); } } catch (InterruptedException e) { // If the thread pool shutdown operation is interrupted during waiting, force shutdown and restore the interrupt state immediately (); ().interrupt(); } } // Custom thread factory class for creating threads private static class CustomThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); // Thread pool number, used to generate thread name private final ThreadGroup group; // Thread group private final AtomicInteger threadNumber = new AtomicInteger(1); // Thread number private final String namePrefix; // Thread name prefix CustomThreadFactory() { // Get the security manager of the current system. If not, use the thread group of the current thread SecurityManager s = (); group = (s != null) ? () : ().getThreadGroup(); // Set the prefix of the thread pool name namePrefix = "pool-" + () + // increment the thread pool number "-thread-"; } // Method to create a new thread public Thread newThread(Runnable r) { // Create a new thread, the thread group, name and priority are all set Thread t = new Thread(group, r, namePrefix + (), 0); // Default priority and daemon settings // If the thread is a daemon thread, set it to a non-daemon thread if (()) (false); // Set thread priority to default if (() != Thread.NORM_PRIORITY) (Thread.NORM_PRIORITY); return t; // Return to the newly created thread } } }
Thread pool initialization:
-
EnhancedExecutorManager
Use of the construction methodThreadPoolExecutor
Create a thread pool, the thread pool size is passedcorePoolSize
Parameter passing. The maximum number of threads in a thread pool is twice that of the core threads. -
LinkedBlockingQueue
Used as a task queue with a size of 1000. If the task volume exceeds the queue capacity, useCallerRunsPolicy
A policy, that is, the task is executed by the thread that submits the task. - Use custom
CustomThreadFactory
To create threads.
Elegantly close thread pool:
-
shutdown()
The method is called first()
to refuse to accept new tasks and then wait for the thread pool to close within the specified timeout. - If the thread pool fails to close normally within the timeout time, then
shutdownNow()
Forces closed and discarded unexecuted tasks while outputting the number of discarded tasks. - If it occurs while waiting for a shutdown
InterruptedException
, it will force the thread pool to be closed and the interrupt state will be restored.
Custom thread factory:
-
CustomThreadFactory
By implementingThreadFactory
Interfaces to define the behavior of creating threads, mainly including thread group, thread name, daemon thread state and thread priority configuration. - The name of each thread follows
pool-number-thread-number
format. The number of thread pools is incremented, and each thread has its own number.
7. Key points to note
- Daemon thread problem: The default created is a non-daemon thread and needs to be explicitly closed
- Interruption policy consistency: The task must implement the correct interrupt handling logic
- Reject strategy cooperation: Responsible configuration of RejectedExecutionHandler
- Resource release order: Database connections and other resources should be closed before the thread pool
- Monitoring mechanism: It is recommended to integrate thread pool monitoring (such as JMX)
8. Advanced application scenarios
Leveled shutdown strategy
public class TieredShutdownManager { // Define a list of thread pools with three priority levels: high priority, medium priority, low priority private final List<ExecutorService> highPriority; private final List<ExecutorService> normalPriority; private final List<ExecutorService> lowPriority; // Public method is used to elegantly close all thread pools public void gracefulShutdown() { // Close high, medium and low priority thread pools in turn shutdownTier(highPriority, 10, ); shutdownTier(normalPriority, 30, ); shutdownTier(lowPriority, 60, ); } // Private method for elegantly closing thread pools with specified priority private void shutdownTier(List<ExecutorService> tier, long timeout, TimeUnit unit) { // Perform a shutdown operation on the specified thread pool list (ExecutorService::shutdown); // Perform an operation waiting for termination on each thread pool, specifying the timeout (executor -> { try { // If the thread pool does not terminate within the timeout time, shutdownNow is called to force close if (!(timeout, unit)) { (); } } catch (InterruptedException e) { // If the thread is interrupted while waiting for termination, restore the interrupt state and force the thread pool to be closed ().interrupt(); (); } }); } }
gracefulShutdown
The method closes the thread pool with high, medium and low priority in order of priority.
shutdownTier
Method first tries to close each thread pool normally (callshutdown
) and then passawaitTermination
The method waits for the thread pool to end within the specified time, and if it does not end successfully, it is calledshutdownNow
Forced to close.
During shutdown, if an interrupt occurs, it will be capturedInterruptedException
Exception, interrupts the current thread, and forces the thread pool to be closed.
9. Performance optimization suggestions
Select a queue policy based on the task type:
- CPU intensive: bounded queue (ArrayBlockingQueue)
- IO intensive: unbounded queue (LinkedBlockingQueue)
Monitoring key indicators:
ThreadPoolExecutor executor = (ThreadPoolExecutor) service; ("Number of active threads: " + ()); ("Number of tasks completed: " + ()); ("Quote Size: " + ().size());
Dynamic adjustment parameters:
(newSize); (newMaxSize);
10. Summary and Suggestions
According to the official Oracle documentation, the following shutdown process is recommended in most production scenarios:
- Priority call to shutdown()
- Set a reasonable awaitTermination timeout
- Call shutdownNow() if necessary
- Always process returned unfinished tasks
- Record a complete shutdown log
The correct choice of closing strategy requires comprehensive consideration:
- Mission importance level
- System resource limitations
- Business Continuity Requirements
- Data consistency requirements
This is the end of this article about how Java elegantly closes the ExecutorService in Asynchronous. For more related content on Java closing the ExecutorService, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!