Panciz
Panciz

Reputation: 2234

ThreadPoolTaskScheduler behaviour when pool is full

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

Answers (3)

Panciz
Panciz

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

Ordous
Ordous

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

M. Deinum
M. Deinum

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

Related Questions