ThreadPoolTaskScheduler behaviour when pool is full

落花浮王杯 提交于 2019-12-12 14:53:36

问题


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


回答1:


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.




回答2:


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())




回答3:


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



来源:https://stackoverflow.com/questions/23111402/threadpooltaskscheduler-behaviour-when-pool-is-full

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!