繁体   English   中英

tomcat关闭时ExecutorService管理的线程会发生什么?

[英]What would happen to the threads managed by ExecutorService when tomcat shutting down?

我有一个在 tomcat 7 上运行的 Web 应用程序(带有 Spring/Spring 引导)。有一些 ExecutorService 定义如下:

    public static final ExecutorService TEST_SERVICE = new ThreadPoolExecutor(10, 100, 60L,
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());

任务很重要,必须正确完成。 我捕获异常并将它们保存到 db 以进行重试,如下所示:

try {
        ThreadPoolHolder.TEST_SERVICE.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    boolean isSuccess = false;
                    int tryCount = 0;
                    while (++tryCount < CAS_COUNT_LIMIT) {
                        isSuccess = doWork(param);
                        if (isSuccess) {
                            break;
                        }
                        Thread.sleep(1000);
                    }
                    if (!isSuccess) {
                        saveFail(param);
                    }
                } catch (Exception e) {
                    log.error("test error! param : {}", param, e);
                    saveFail(param);
                }
            }
        });
    } catch (Exception e) {
        log.error("test error! param:{}", param, e);
        saveFail(param);
    }

那么,当tomcat关闭时,池中的线程(在队列中运行或等待)会发生什么? 如何确保所有任务在关机前正确完成或保存到数据库重试?

Tomcat 作为任何 Java 应用程序都不会结束,直到所有非 daeon 线程都结束。 上例中的 ThreadPoolExecutor 使用默认线程工厂并将创建非守护线程。

Tomcat 内置了 Thread Leak 检测,因此在取消部署应用程序时您应该会收到错误消息。 作为开发人员,您有责任将您创建的任何对象链接到 Web 应用程序生命周期,这意味着您永远不应该拥有不是常量的静态状态

如果您使用的是 Spring Boot,则您的 Spring 上下文已经链接到应用程序生命周期,因此最好的方法是将您的执行程序创建为 Spring bean,并在应用程序停止时让 Spring 关闭它。 这是一个可以放在任何 @Configuration 类中的示例。

@Bean(destroyMethod = "shutdownNow", name = "MyExecutorService")
public ThreadPoolExecutor executor() {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 60L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), 
            new ThreadPoolExecutor.CallerRunsPolicy());
    return threadPoolExecutor;
}

如您所见,@Bean 批注允许您指定一个 destroy 方法,该方法将在 Spring 上下文关闭时执行。 此外,我还添加了 name 属性,这是因为 Spring 通常会为异步 Web 处理等内容创建许多 ExecutorServices。 当您需要使用 executor 时,只需像任何其他 spring bean 一样自动装配它。

@Autowired
@Qualifier(value = "MyExecutorService")
ThreadPoolExecutor executor;

记住静态是邪恶的,你应该只对常量和潜在的不可变对象使用静态。

编辑

如果你需要阻塞Tomcats关闭程序直到任务处理完毕,你需要将Executor包裹在一个组件中以获得更多控制,就像这样。

@Component
public class ExecutorWrapper implements DisposableBean {

    private final ThreadPoolExecutor threadPoolExecutor;

    public ExecutorWrapper() {
        threadPoolExecutor = new ThreadPoolExecutor(10, 100, 60L,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
    }

    public <T> Future<T> submit(Callable<T> task) {
        return threadPoolExecutor.submit(task);
    }

    public void submit(Runnable runnable) {
        threadPoolExecutor.submit(runnable);
    }

    @Override
    public void destroy() throws Exception {
        threadPoolExecutor.shutdown();
        boolean terminated = threadPoolExecutor.awaitTermination(1, TimeUnit.MINUTES);
        if (!terminated) {
            List<Runnable> runnables = threadPoolExecutor.shutdownNow();
            // log the runnables that were not executed
        }
    }
}

使用此代码,您首先调用shutdown以便无法提交新任务,然后等待执行器完成当前任务并排队等待一段时间。 如果它没有及时完成,您可以调用shutdownNow中断正在运行的任务,并获取未处理的任务列表。

注意: DisposableBean 可以解决问题,但最好的解决方案实际上是实现SmartLifecycle接口。 您必须实现更多的方法,但您会获得更大的控制权,因为在所有 bean 都被实例化并且整个 bean 层次结构连接在一起之前,不会启动任何线程,它甚至允许您指定应该以何种顺序启动组件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM