一、引言
今天同事提了一个问题,每次系统发布的时候在老的节点服务里面的线程池会怎么样,队列里面的任务会被执行完毕吗?
博主一直认为优雅退出一定是会等执行完才退出的,结果跟chatGpt聊了聊,一波三折。
二、分析
1、ThreadPoolExecutor的退出
博主问的是:java服务退出的时候,线程池的队列中的任务会怎么样?
chatGpt回答:当 Java 服务退出时,线程池中尚未执行的任务(队列中的任务)的处理方式取决于线程池的类型和配置。对于标准的 ThreadPoolExecutor
,当 Java 服务退出时,线程池会尝试完成尚未执行的任务。它会等待队列中的任务被执行完毕,并逐个处理每个任务,直到队列为空。这意味着线程池会等待所有已提交的任务完成,然后关闭线程池。
默认情况下,ThreadPoolExecutor
使用的是非守护线程,因此线程池中的线程不会阻止 Java 服务的退出,但会继续执行任务直到队列为空。如果你想要在 Java 服务退出时立即停止线程池中的任务,可以将线程池的线程设置为守护线程(通过设置 setDaemon(true)
),这样当主线程退出时,守护线程会被中断并停止任务的执行。
这么一看,跟博主的想法是一致的,但是博主又想了一个问题,一般ThreadPoolExecutor
都是交给spring托管的,还得考虑一下spring。
@Bean
public ThreadPoolExecutor threadPoolExecutor() {
// 创建自定义的 ThreadPoolExecutor
// ...
}
2、spring托管ThreadPoolExecutor的退出
博主问的是:Spring Boot不会等@bean定义的ThreadPoolExecutor执行完任务吗?
chatGpt回答的是:在 Spring Boot 中,使用 @Bean
定义的 ThreadPoolExecutor
默认情况下不会等待其中的任务执行完毕。当 Spring Boot 应用程序关闭时,它会触发关闭钩子,自动关闭 Spring 容器中的所有对象,包括通过 @Bean
定义的线程池。
默认情况下,Spring Boot 会调用线程池的 shutdown()
方法来关闭线程池,但不会等待其中的任务全部执行完毕。正在执行的任务会继续执行,而尚未执行的任务将会被丢弃。
这就惊呆博主了,这样搞那不是队列堆的数据全完了,发布一次如果碰到一些流量,流量全是废掉。但是真的会这样吗?
3、shutdownNow与shutdown的区别
既然spring用ThreadPoolExecutor
的shutdown方法关闭的线程池,那么回归源码看看吧,结果一看还有个shutdownNow方法,这明显带了点暴力和优雅的区别。
看看两个方法之间的区别
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
比较大的区别是:
1、shutdownNow会取出任务返回
2、shutdownNow会通过cas设置STOP状态,而shutdown会设置SHUTDOWN状态
很明显区别在这个状态,那么这个状态是不是影响了队列任务呢,看一下获取队列任务方法
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
很明显华点在于if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())),当STOP的时候一定不会再返回任务给worker,但是SHUTDOWN的时候,队列为空才不返回,因此shutdown方法会继续执行队列中的任务,这也就和chatGpt说的产生了不同的结论!
4、bean的destroyMethod
但是还有个问题,对象的销毁方法,一般是会在对象上指定shutdown
@Bean(name = "handoverExecutor", destroyMethod = "shutdown")
public ThreadPoolExecutor handoverExecutor() {
return new ThreadPoolExecutor(20, 30,
600, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.DiscardPolicy());
}
但是也有时候会不指定,走默认的,那么默认的是什么呢
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
public static final String INFER_METHOD = "(inferred)";
这个inferred是什么方法,跟踪一下反射取方法的地方,可以看到没有close就取shutdown方法,而ThreadPoolExecutor是没有close方法的,因此它默认走shutdown
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && closeableInterface.isInstance(bean))) {
// Only perform destroy method inference or Closeable detection
// in case of the bean not explicitly implementing DisposableBean
if (!(bean instanceof DisposableBean)) {
try {
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
三、总结
chatGpt在单一方面的知识搜索的确是比较深入精准,相对于直接从百度、谷歌、技术社区过滤大量的言论要好很多,可以说是一个优秀的AI搜索引擎。
但是chatGpt的弱点在于不会分析,一旦结合多方面的知识,给出的信息可能是错的,各位同学还是多看权威书籍和源码、技术社区多交流,不用一味相信搜索引擎。