在tomcat应用里添加了一个线程池对象,向这个线程池发送任务,让其执行。 我希望在tomcat停机时,能等待线程池里的任务执行完了再停机,要如何实现?

核心思路

  1. 项目启动时创建全局单例线程池
  2. 实现 ServletContextListener
  3. 停机时调用线程池的优雅关闭方法 :
    • shutdown()
    • awaitTermination(...) 等待任务执行完
  4. 超时未关闭则强制关闭 shutdownNow()

完整实现代码

1. 线程池单例(建议放在工具类)

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TaskPool {

    // 全局唯一线程池
    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(
            5,
            10,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100)
    );

    public static ExecutorService getExecutor() {
        return EXECUTOR;
    }
}
  1. 编写监听器(关键)
java 复制代码
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@WebListener // 让 Tomcat 自动识别
public class ThreadPoolShutdownListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 应用启动时可做初始化
        System.out.println("Tomcat 应用启动");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Tomcat 停机,开始关闭线程池...");

        ExecutorService executor = TaskPool.getExecutor();

        // 1. 拒绝新任务
        executor.shutdown();

        try {
            // 2. 等待已提交任务执行完毕(例如等30秒)
            if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                System.err.println("线程池超时未关闭,强制关闭");
                // 3. 超时则强制关闭
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.err.println("关闭线程池被中断");
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }

        System.out.println("线程池已安全关闭");
    }
}
  1. 如果不用 @WebListener,就在 web.xml 配置
XML 复制代码
<listener>
    <listener-class>com.xxx.xxx.ThreadPoolShutdownListener</listener-class>
</listener>

为什么这样能保证 Tomcat 等任务执行完?

  • Tomcat 停止时会按顺序调用所有 ServletContextListener 的 contextDestroyed
  • awaitTermination(30, SECONDS) 会阻塞当前线程 ,直到:
    • 所有任务执行完
    • 或超时
  • 阻塞期间 Tomcat 不会继续销毁 Filter、Servlet,实现等待效果

重要注意点

  1. 不要用 Executors.newFixedThreadPool 队列无界,可能堆积太多任务导致停机很久。
  2. awaitTermination 时间要合理 一般 10~60 秒,根据你的任务耗时设置。
  3. 你的任务最好能响应中断 否则 shutdownNow() 也杀不掉,会一直卡到超时。
  4. 不要在定时任务 / 异步里无限循环否则永远关不掉。

参考文献:豆包

相关推荐
齐 飞4 小时前
idea中git误操作撤回git reset --hard ‘HEAD@{1}‘
java·git·intellij-idea
你的保护色4 小时前
多AS互联BGP4综合实验设计
java
某不知名網友5 小时前
ROS2进阶:服务通信、自定义SRV接口、参数服务器与基于服务的参数通信详解
java·开发语言·microsoft
Wang's Blog5 小时前
Java 项目实战: 交友项目 - MongoDB 入门:体系结构和命令行操作
java·mongodb·交友
小溪学编程5 小时前
将 Bean 放入 Spring 容器中的方式有哪些?
java·后端·spring
java_nnnn5 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
用户3721574261355 小时前
Java 读取、修改和清空 PDF 文档属性(元数据)
java
SL_staff5 小时前
从‘改一行代码等三天’到‘改完即生效’:实施工程师的逻辑引擎避坑手记
java·产品·全栈
小羊没烦恼!5 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
hhwyqwqhhwy5 小时前
设备树之 provider 和 consumer
java·linux·前端