在日常使用线程池的过程中,怎样合理地关闭线程池,最小程度地影响业务,shutdown 和 shutdownNow 该如何选择?

 1     public void shutdown() {
 2         final ReentrantLock mainLock = this.mainLock;
 3         mainLock.lock();
 4         try {
 5             checkShutdownAccess();
 6             advanceRunState(SHUTDOWN);
 7             interruptIdleWorkers();
 8             onShutdown(); // hook for ScheduledThreadPoolExecutor
 9         } finally {
10             mainLock.unlock();
11         }
12         tryTerminate();
13     }
 1     public List<Runnable> shutdownNow() {
 2         List<Runnable> tasks;
 3         final ReentrantLock mainLock = this.mainLock;
 4         mainLock.lock();
 5         try {
 6             checkShutdownAccess();
 7             advanceRunState(STOP);
 8             interruptWorkers();
 9             tasks = drainQueue();
10         } finally {
11             mainLock.unlock();
12         }
13         tryTerminate();
14         return tasks;
15     }

对比过后,两个方法,设置的线程池状态不同,分别为 SHUTDOWN 和 STOP,中断线程的细节不同,分别是中断 idle 工作线程和中断所有工作线程。

怎么判断工作线程是否空闲呢?原来工作线程执行任务的时候,会加锁,任务执行完成后释放锁。

 1     final void runWorker(Worker w) {
 2         Thread wt = Thread.currentThread();
 3         Runnable task = w.firstTask;
 4         w.firstTask = null;
 5         w.unlock(); // allow interrupts
 6         boolean completedAbruptly = true;
 7         try {
 8             while (task != null || (task = getTask()) != null) {
 9                 w.lock();
10                 // If pool is stopping, ensure thread is interrupted;
11                 // if not, ensure thread is not interrupted.  This
12                 // requires a recheck in second case to deal with
13                 // shutdownNow race while clearing interrupt
14                 if ((runStateAtLeast(ctl.get(), STOP) ||
15                      (Thread.interrupted() &&
16                       runStateAtLeast(ctl.get(), STOP))) &&
17                     !wt.isInterrupted())
18                     wt.interrupt();
19                 try {
20                     beforeExecute(wt, task);
21                     try {
22                         task.run();
23                         afterExecute(task, null);
24                     } catch (Throwable ex) {
25                         afterExecute(task, ex);
26                         throw ex;
27                     }
28                 } finally {
29                     task = null;
30                     w.completedTasks++;
31                     w.unlock();
32                 }
33             }
34             completedAbruptly = false;
35         } finally {
36             processWorkerExit(w, completedAbruptly);
37         }
38     }

中断 idle 线程时,尝试获取锁,获取锁成功则认定线程 idle

 1     private void interruptIdleWorkers(boolean onlyOne) {
 2         final ReentrantLock mainLock = this.mainLock;
 3         mainLock.lock();
 4         try {
 5             for (Worker w : workers) {
 6                 Thread t = w.thread;
 7                 if (!t.isInterrupted() && w.tryLock()) {
 8                     try {
 9                         t.interrupt();
10                     } catch (SecurityException ignore) {
11                     } finally {
12                         w.unlock();
13                     }
14                 }
15                 if (onlyOne)
16                     break;
17             }
18         } finally {
19             mainLock.unlock();
20         }
21     }

工作线程 idle,它必然阻塞在 getTask 处,它被中断后,会根据线程池状态返回 null task,完成工作线程的退出。

 1     final void runWorker(Worker w) {
 2         Thread wt = Thread.currentThread();
 3         Runnable task = w.firstTask;
 4         w.firstTask = null;
 5         w.unlock(); // allow interrupts
 6         boolean completedAbruptly = true;
 7         try {
 8             while (task != null || (task = getTask()) != null) {
 9                 w.lock();
10                 // If pool is stopping, ensure thread is interrupted;
11                 // if not, ensure thread is not interrupted.  This
12                 // requires a recheck in second case to deal with
13                 // shutdownNow race while clearing interrupt
14                 if ((runStateAtLeast(ctl.get(), STOP) ||
15                      (Thread.interrupted() &&
16                       runStateAtLeast(ctl.get(), STOP))) &&
17                     !wt.isInterrupted())
18                     wt.interrupt();
19                 try {
20                     beforeExecute(wt, task);
21                     try {
22                         task.run();
23                         afterExecute(task, null);
24                     } catch (Throwable ex) {
25                         afterExecute(task, ex);
26                         throw ex;
27                     }
28                 } finally {
29                     task = null;
30                     w.completedTasks++;
31                     w.unlock();
32                 }
33             }
34             completedAbruptly = false;
35         } finally {
36             processWorkerExit(w, completedAbruptly);
37         }
38     }

总结:

shutdown 无法提交新任务,中断空闲工作线程,等待队列中的任务执行完毕。

shutdownNow 无法提交新任务,中断所有线程,返回队列中的任务。

可以看出,shutdown 方法已经足够优雅了,那么还有哪些地方需要增强呢?如果线程池队列中堆积的任务太多,导致线程池关闭的时间太长,可以手动地将队列中的任务全部移除(当然前提是可以获取到队列的引用),然后再关闭线程池;同时还可以结合 awaitTermination 方法等待线程池关闭。如果是应用程序停止时关闭线程池,需要注意如果线程池线程是后台线程,则有可能导致关闭异常,在前台线程中调用 shutdown 后,接着调用 awaitTermination 进行等待。

那什么时候可以使用 shutdownNow 呢,如果要快速关闭线程池,用这个方法似乎更优,但是它会中断工作线程,而我们的业务代码不确定哪个地方会阻塞(RPC 调用,加锁等待,读取数据库),很多阻塞代码可能会响应中断,抛出异常,导致任务执行异常,所以慎用。

 

posted on 2024-06-28 09:44  偶尔发呆  阅读(3)  评论(0编辑  收藏  举报