Reputation: 2234
I need to implement a service that read periodically a directory and process the file found in it.
I want to read the directory pretty often, each 5 seconds for example. The problem is that sending the files could take several time. When the files are sent a moved them away.
My idea is to use ThreadPoolTaskScheduler
with a single thread pool and shedule the task to run each 5 seconds.
The solution seems to work, I read often the directory and when is empty the task finished quickly. When I found some files the task takes longer but since there pool is one thread only no other concurrent tasks are executed.
My problem is that I can't understand what happened to the tasks scheduled that cannot run because the pool is full? are they canceled or just enqueued. I'm worried to enqueue too much tasks and create memory leaks.
This is my 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>
and java code.
TaskScheduler ts = appContext.getBean(TaskScheduler.class);
InviaFattureTask task = appContext.getBean(InviaFattureTask.class);
ts.schedule(task, new CronTrigger("*/5 * * * * ?"));
Thanks Davide
Upvotes: 3
Views: 7596
Reputation: 2234
At the end I choose this configuration:
<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>
The semantic is almost the same but the configuration is more explicit.
I use cron since instead of (scheduleWithFixedDelay(), scheduleAtFixedRate())
I plan to have several installations and I want to be free to configure the trigger in many different ways.
Thanks
Upvotes: 0
Reputation: 3884
Quick test would tell you what is happening:
XML:
<task:scheduler id="taskScheduler" pool-size="1"/>
And 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 * * * * ?"));
Output:
Job start: 1397660360001
Job end: 1397660370001
Job start: 1397660375000
Job end: 1397660385000
Job start: 1397660390001
Job end: 1397660400001
As for explanation, the source code for ThreadPoolTaskScheduler
and ReshcedulingRunnable
explains it quite well:
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();
}
}
I.E. it schedules the task, and when it ends - finds the next execution trigger and schedules it then using a simple delay. The number of threads in your executor does not matter, next execution will not start until the previous one has finished (you can test the above with 2 threads to confirm).
Also, since you seem to want to run this task periodically - Why not use a ScheduledTaskExecutor
that has 2 methods for your exact use case? (scheduleWithFixedDelay()
, scheduleAtFixedRate()
)
Upvotes: 1
Reputation: 124441
The default policy used when using the <task:scheduler />
tag is to abort the new task and throw a RejectedExecutionException
.
If you use a plain bean to configure the ThreadPoolTaskScheduler
you are able to set a few more properties which can influence that behavior, the chosen 'executor' and 'rejectedExecutionHandler` play a big role in that.
Also you are using java to configure your task, you can move that to xml.
<task:scheduled-tasks>
<task:scheduled ref="inviaFattureTask" cron="*/5 * * * * ?"/>
</task:scheduled-tasks>
For more information check the reference guide.
Upvotes: 1