跟chatGpt 聊聊发布的时候老节点的线程池会怎么样

一、引言

        今天同事提了一个问题,每次系统发布的时候在老的节点服务里面的线程池会怎么样,队列里面的任务会被执行完毕吗?

c78350c33c994504a4655800ee0ad1cb.png

        博主一直认为优雅退出一定是会等执行完才退出的,结果跟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的弱点在于不会分析,一旦结合多方面的知识,给出的信息可能是错的,各位同学还是多看权威书籍和源码、技术社区多交流,不用一味相信搜索引擎。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖当当技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值