我需要实现一个定期读取目录并处理其中找到的文件的服务。
我想经常阅读目录,例如每5秒。问题是发送文件可能需要几个时间。发送文件后,将它们移走。
我的想法是将ThreadPoolTaskScheduler
与单个线程池一起使用,并将任务转换为每5秒运行一次。
解决方案似乎有效,我经常读取目录,当空的时候任务快速完成。当我找到一些文件时,任务需要更长的时间,但由于池只有一个线程,所以没有其他并发任务被执行。
我的问题是我无法理解由于池已满而无法运行的计划任务发生了什么?他们被取消或刚入队。我担心将太多任务排入队列并造成内存泄漏。
这是我的spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd">
....
<bean id="inviaFattureTask" class="com.sirio.fatturazionepa.invio.InviaFattureTask">
<property name="fileSystemUtils" ref="fileSystemUtils"></property>
<property name="fattureManager" ref="fattureManager"></property>
</bean>
<task:scheduler id="myScheduler" pool-size="1"/>
...
</beans>
和java代码。
TaskScheduler ts = appContext.getBean(TaskScheduler.class);
InviaFattureTask task = appContext.getBean(InviaFattureTask.class);
ts.schedule(task, new CronTrigger("*/5 * * * * ?"));
感谢Davide
答案 0 :(得分:1)
使用<task:scheduler />
标记时使用的默认策略是中止新任务并抛出RejectedExecutionException
。
如果使用普通bean来配置ThreadPoolTaskScheduler
,您可以设置一些可以影响该行为的属性,所选择的“executor”和“rejectedExecutionHandler”在这方面发挥了重要作用。
此外,您使用java配置任务,您可以将其移至xml。
<task:scheduled-tasks>
<task:scheduled ref="inviaFattureTask" cron="*/5 * * * * ?"/>
</task:scheduled-tasks>
有关详细信息,请查看reference guide。
答案 1 :(得分:1)
快速测试会告诉你发生了什么:
XML:
<task:scheduler id="taskScheduler" pool-size="1"/>
和Java:
test.schedule(new Runnable() {
@Override
public void run() {
long ts = System.currentTimeMillis();
System.out.println("Job start: " + ts);
long counter = 0;
while (System.currentTimeMillis() - ts < 10000) {
counter++;
}
System.out.println("Job end: " + System.currentTimeMillis());
}
}, new CronTrigger("*/5 * * * * ?"));
输出:
工作开始:1397660360001
工作结束:1397660370001
工作开始:1397660375000
工作结束:1397660385000
工作开始:1397660390001
工作结束:1397660400001
至于解释,ThreadPoolTaskScheduler
和ReshcedulingRunnable
的源代码解释得很清楚:
public ScheduledFuture schedule() {
synchronized (this.triggerContextMonitor) {
this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
if (this.scheduledExecutionTime == null) {
return null;
}
long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis();
this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
return this;
}
}
@Override
public void run() {
Date actualExecutionTime = new Date();
super.run();
Date completionTime = new Date();
synchronized (this.triggerContextMonitor) {
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
}
if (!this.currentFuture.isCancelled()) {
schedule();
}
}
即。它调度任务,当它结束时 - 找到下一个执行触发器并使用简单的延迟调度它。执行程序中的线程数无关紧要,下一次执行直到前一个执行完毕才会启动(您可以使用2个线程进行上述测试以确认)。
此外,由于您似乎想要定期运行此任务 - 为什么不使用具有2种方法的ScheduledTaskExecutor
用于您的确切用例? (scheduleWithFixedDelay()
,scheduleAtFixedRate()
)
答案 2 :(得分:0)
最后我选择了这个配置:
<task:scheduler id="myScheduler" pool-size="1"/>
<task:executor id="executor" rejection-policy="ABORT"/>
<task:scheduled-tasks scheduler="myScheduler" >
<task:scheduled ref="inviaFattureTask" method="run" cron="*/5 * * * * ?"/>
</task:scheduled-tasks>
语义几乎相同,但配置更明确。
我使用cron而不是(scheduleWithFixedDelay(), scheduleAtFixedRate())
我计划进行多次安装,我希望可以通过多种方式自由配置触发器。
由于