[英]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.