gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 02:29:32 25 4
gpt4 key购买 nike

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

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

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

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 内置了线程泄漏检测,因此当应用程序取消部署时,您应该会收到错误消息。作为开发人员,您有责任将您创建的任何对象链接到 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。当您需要使用执行器时,只需像任何其他 spring bean 一样 Autowiring 它即可。

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

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

编辑

如果您需要阻止 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 接口(interface)。您必须实现更多方法,但您可以获得更大的控制权,因为在所有 bean 实例化并且整个 bean 层次结构连接在一起之前不会启动任何线程,它甚至允许您指定应该启动组件的顺序。

关于java - 当tomcat关闭时ExecutorService管理的线程会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44582208/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com