版权声明:本文为博主原创文章,如需转载,请标明出处。 https://blog.csdn.net/alan_liuyue/article/details/85002896
简介
- spring内部整合quartz,将quartz整合到web项目里面,通过页面动态控制quartz的增加、修改、删除、查询,这种方式极大简化了对quartz定时器任务的控制;
- 但随之而来的是一个极为困扰的问题:当项目的服务器关闭的时候,quartz定时器任务进程依旧在运行,如果不手动去kill掉进程,这个定时任务会一直残留,导致下次再重启服务器的时候,又重复启动定时器,导致多个重复的定时任务在运行;
- 问题展现:
warnning: The web application [xxxxx] appears to have started a thread named [DefaultQuartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.lang.Object.wait(Native Method)
org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:568)
解决方案
- 使用监听器关闭quartz的定时器进程;
- 在尝试网上多种解决方案无果之后,从中找到了一些灵感,在关闭tomcat的时候,触发监听器,调用方法获取当前进程的所有quartz定时器线程,然后强制执行shutdown方法,删除所有quartz线程,虽然非最佳方案,但确实解决了我所头疼的定时器无法在服务器关闭的时候自动关闭的问题;
- 网友解决方案相关链接:
- https://blog.csdn.net/only09080229/article/details/42172251
- https://www.cnblogs.com/passedbylove/p/7580477.html
- https://www.linuxidc.com/Linux/2015-06/119042.htm
- https://blog.csdn.net/dslztx/article/details/47276953
- https://blog.csdn.net/liujun_for_java/article/details/78101478?utm_source=blogxgwz5
代码实践
- 首先在web.xml添加监听器的类映射(如果项目的webapp版本是3.0以上的话可以直接在类上添加@WebListener即可):
<listener>
<listener-class>com.sixmonth.common.listener.AppContextListener</listener-class>
</listener>
- 创建监听器java类:
package com.sixmonth.common.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Component;
/**
* tomcat开启以及关闭的监听器
* @author hqc
* @Date: 2018年12月13日
*
*/
@Component
public class AppContextListener implements ServletContextListener{
/**
* tomcat启动初始化
*/
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("tomcat已经启动!");
}
/**
* tomcat关闭
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("tomcat已经关闭!开始关闭quartz!");
try {
SchedulerFactory sf = new StdSchedulerFactory();//创建新的调度器工厂
Scheduler scheduler = null;
scheduler = sf.getScheduler();//获取当前进程的所有定时器线程数据
scheduler.shutdown(false);//关闭定时器线程
System.out.println("关闭定时器线程成功!");
} catch (Exception e) {
System.out.println("关闭定时器线程失败!");
e.printStackTrace();
}
}
}
结语
网上解决此类问题的方法有很多种,有兴趣的程序员可深入了解,如果有更有效的方法欢迎和小编交流,不胜感激~