一開始是使用Spring自帶的定時器來配置定時任務的,簡單快捷,配置如下:
<bean id="refreshCache" class="com.gionee.baserom.search.job.RefreshCache" />
<task:scheduled-tasks>
<task:scheduled ref="refreshCache" method="execute" cron="0 */30 * * * ?"/>
</task:scheduled-tasks>
但是使用一段時間之後就無故停止,且不報錯,所以沒有相關錯誤日誌,需要重啓Tomcat之後才能繼續執行定時任務。
開始以爲由於數據庫最大連接數的限制,設置成翻倍了之後仍出現這問題。在同學的提醒下意識到可能是線程阻塞導致,於是網上查到原因:
Spring定時任務默認都是併發執行的,不會等待上一次任務執行完畢,只要間隔時間到就會執行。
解決方案:
1.將JobDetail的concurrent屬性配置爲false。不允許任務併發執行。
2.任務執行時間較長時,查找根本問題。
於是把Spring自帶的定時器改用Quartz,依賴相關包:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
定時任務配置如下:
<!-- 工作的bean -->
<bean id="myJob" class=" com.gionee.baserom.exchangerate.job.DailyTaskJob" />
<!-- 定義任務,爲了避免線程阻塞,用concurrent=false -->
<bean id="myJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myJob" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" />
</bean>
<!-- 配置觸發器 -->
<bean id="myJobTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJobDetail" />
<property name="cronExpression" value="0 0/30 * * * ?" />
</bean>
<!-- 配置調度器 -->
<bean name="startQuertz" lazy-init="false" autowire="no" destroy-method="destroy"
class="com.gionee.baserom.exchangerate.util.SchedulerFactoryBeanWithShutdownDelay" >
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.threadCount">1</prop>
</props>
</property>
<property name="waitForJobsToCompleteOnShutdown">
<value>false</value>
</property>
<property name="triggers">
<list>
<ref bean="myJobTrigger" />
</list>
</property>
</bean>
在startQuartz中用到SchedulerFactoryBeanWithShutdownDelay是因爲當Tomcat被關閉時,有可能導致任務線程並未完全關閉,導致內存泄漏。
SchedulerFactoryBeanWithShutdownDelay.java
import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
public class SchedulerFactoryBeanWithShutdownDelay extends SchedulerFactoryBean {
@Override
public void destroy() throws SchedulerException {
super.destroy();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}