When using the thread pool, after calling the shutdown() method, the thread pool will no longer accept new execution tasks. However, tasks placed in the task queue before calling the shutdown() method still need to be executed. This method is a non-blocking method and will return immediately after being called. It will not wait for all tasks in the task queue to be executed before returning. Let’s take a look at the source code of the shutdown() method, as shown below.
public void shutdown() { //获取线程池的全局锁 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查是否有关闭线程池的权限 checkShutdownAccess(); //将当前线程池的状态设置为SHUTDOWN advanceRunState(SHUTDOWN); //中断Worker线程 interruptIdleWorkers(); //为ScheduledThreadPoolExecutor调用钩子函数 onShutdown(); // hook for } finally { //释放线程池的全局锁 mainLock.unlock(); } //尝试将状态变为TERMINATED tryTerminate(); }
Generally speaking, the code of the shutdown() method is relatively simple. It first checks whether there is permission to close the thread pool. If there is permission, then it checks again whether there is permission to interrupt the working thread. If there is no permission, A SecurityException will be thrown, and the code is as follows.
//检查是否有关闭线程池的权限 checkShutdownAccess(); //将当前线程池的状态设置为SHUTDOWN advanceRunState(SHUTDOWN); //中断Worker线程 interruptIdleWorkers();
Among them, the implementation code of the checkShutdownAccess() method is as follows.
private void checkShutdownAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(shutdownPerm); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) security.checkAccess(w.thread); } finally { mainLock.unlock(); } } }
It is relatively simple to understand the code of the checkShutdownAccess() method, which is to detect whether you have permission to close the thread pool, during which the global lock of the thread pool is used.
Next, we look at the source code of the advanceRunState(int) method, as shown below.
private void advanceRunState(int targetState) { for (;;) { int c = ctl.get(); if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) break; } }
The overall logic of the advanceRunState(int) method is to determine whether the current thread pool state is the specified state. The state passed in the shutdown() method is SHUTDOWN. If it is SHUTDOWN, it will be returned directly; if If it is not SHUTDOWN, set the status of the current thread pool to SHUTDOWN.
Next, let’s look at the interruptIdleWorkers() method called by the showdown() method, as shown below.
private void interruptIdleWorkers() { interruptIdleWorkers(false); }
You can see that the interruptIdleWorkers() method calls the interruptIdleWorkers(boolean) method. Continue to look at the source code of the interruptIdleWorkers(boolean) method, as shown below.
private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) { Thread t = w.thread; if (!t.isInterrupted() && w.tryLock()) { try { t.interrupt(); } catch (SecurityException ignore) { } finally { w.unlock(); } } if (onlyOne) break; } } finally { mainLock.unlock(); } }
The overall logic of the above code is: obtain the global lock of the thread pool, cycle through all worker threads, detect whether the thread is interrupted, if not interrupted, and the Worker thread obtains the lock, execute the thread's interruption method and release the lock acquired by the thread. At this time, if the onlyOne parameter is true, exit the loop. Otherwise, loop through all worker threads and perform the same operation. Finally, the global lock of the thread pool is released.
Next, let’s take a look at the shutdownNow() method.
If the shutdownNow() method of the thread pool is called, the thread pool will no longer accept new execution tasks and will also discard the tasks existing in the task queue. The executing Worker thread will also be interrupted immediately. At the same time, the method will return immediately. This method has a return value, which is the list of discarded tasks in the current task queue.
The source code of the shutdownNow() method is as follows.
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查是否有关闭权限 checkShutdownAccess(); //设置线程池的状态为STOP advanceRunState(STOP); //中断所有的Worker线程 interruptWorkers(); //将任务队列中的任务移动到tasks集合中 tasks = drainQueue(); } finally { mainLock.unlock(); } /尝试将状态变为TERMINATED tryTerminate(); //返回tasks集合 return tasks; }
The overall logic of the source code of the shutdownNow() method is basically the same as the shutdown() method, except that the shutdownNow() method sets the status of the thread pool to STOP, interrupts all Worker threads, and puts it in the task queue All tasks are moved to the tasks collection and returned.
It can be seen that when the shutdownNow() method interrupts all threads, the interruptWorkers() method is called. Next, we will look at the source code of the interruptWorkers() method, as shown below.
private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) w.interruptIfStarted(); } finally { mainLock.unlock(); } }
The logic of the interruptWorkers() method is relatively simple, which is to obtain the global lock of the thread pool, cycle through all worker threads, interrupt the threads in sequence, and finally release the global lock of the thread pool.
Inside the interruptWorkers() method, the interruptIfStarted() method of the Worker class is actually called to interrupt the thread. Let’s look at the source code of the interruptIfStarted() method of the Worker class, as shown below.
void interruptIfStarted() { Thread t; if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }
I found that it essentially calls the interrupt() method of the Thread class to interrupt the thread.
When the thread pool calls the awaitTermination(long, TimeUnit) method, the caller's thread will be blocked until the status of the thread pool is changed to TERMINATED. Return, or return when the timeout period is reached. Next, let's look at the source code of the awaitTermination(long, TimeUnit) method, as shown below.
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { //获取距离超时时间剩余的时长 long nanos = unit.toNanos(timeout); //获取Worker线程的的全局锁 final ReentrantLock mainLock = this.mainLock; //加锁 mainLock.lock(); try { for (;;) { //当前线程池状态为TERMINATED状态,会返回true if (runStateAtLeast(ctl.get(), TERMINATED)) return true; //达到超时时间,已超时,则返回false if (nanos <= 0) return false; //重置距离超时时间的剩余时长 nanos = termination.awaitNanos(nanos); } } finally { //释放锁 mainLock.unlock(); } }
The overall logic of the above code is: first obtain the exclusive lock of the Worker thread, and then loop to determine whether the current thread pool is already in the TERMINATED state. If so, return true directly, otherwise check whether it has timed out. If it has If it times out, it returns false. If it does not time out, reset the remaining time before the timeout. Next, enter the next cycle and check again whether the current thread pool is in the TERMINATED state. If so, return true directly. Otherwise, check whether it has timed out. If it has timed out, return false. If it does not time out, reset the remaining time before the timeout. This loop continues until the status of the thread pool becomes TERMINATED or has timed out.
The above is the detailed content of How to shut down the Java thread pool gracefully?. For more information, please follow other related articles on the PHP Chinese website!