Spring任务执行和调度task:scheduler与task:executor的配置
配置说明:
从Spring 3.0开始,有一个用于配置TaskExecutor和TaskScheduler实例的XML命名空间。它还提供了一种方便的方法来配置要使用触发器安排的任务。
任务调度器的配置详细参数说明:
task:scheduler/@pool-size:调度线程池的大小,调度线程在被调度任务完成前不会空闲
task:scheduled/@cron:cron表达式,注意,若上次任务未完成,即使到了下一次调度时间,任务也不会重复调度
<task:scheduled-tasks scheduler="scheduler"> <task:scheduled ref="beanID" method="methodName" cron="CronExp" /> </task:scheduled-tasks> <task:scheduler id="scheduler" pool-size="10" />
任务执行器配置详细参数说明:
task:executor/@pool-size:可以指定执行线程池的初始大小、最大大小
task:executor/@queue-capacity:等待执行的任务队列的容量
task:executor/@rejection-policy:当等待队列爆了时的策略,分为丢弃、由任务执行器直接运行等方式
<task:executor id="executor" keep-alive="3600" pool-size="100-200" queue-capacity="500" rejection-policy="CALLER_RUNS" />
从该配置中可以看出,还提供了“队列容量”值。还应根据执行程序的队列容量来考虑线程池的配置。有关池大小和队列容量之间关系的完整描述,请参阅ThreadPoolExecutor的文档。主要思想是,当提交任务时,如果活动线程的数量当前小于核心大小,则执行程序将首先尝试使用空闲线程。如果已达到核心大小,则只要尚未达到其容量,任务就会添加到队列中。只有这样,如果已达到队列的容量,执行程序是否会创建超出核心大小的新线程。如果还达到了最大大小,则执行程序将拒绝该任务。
默认情况下,队列是无限制的,但这很少是所需的配置,因为如果在所有池线程忙的情况下将足够的任务添加到该队列,则可能导致OutOfMemoryErrors。此外,如果队列是无界的,那么最大大小根本没有影响。由于执行器将始终在创建超出核心大小的新线程之前尝试队列,因此队列必须具有有限的容量,以使线程池增长超出核心大小(这就是为什么固定大小的池是使用时唯一合理的情况一个无限的队列)。
稍后,我们将回顾保持活动设置的效果,这增加了在提供池大小配置时要考虑的另一个因素。首先,如上所述,让我们考虑一个任务被拒绝的情况。默认情况下,当任务被拒绝时,线程池执行程序将抛出TaskRejectedException。但是,拒绝策略实际上是可配置的。使用默认拒绝策略(AbortPolicy实现)时会抛出异常。对于可以在高负载下跳过某些任务的应用程序,可以配置DiscardPolicy或DiscardOldestPolicy。另一个适用于需要在高负载下限制提交的任务的应用程序的选项是CallerRunsPolicy。该策略不会抛出异常或丢弃任务,而只是强制调用submit方法的线程自己运行任务。这个想法是这样的调用者在运行该任务时会很忙,而不能 立即提交其他任务。 因此,它提供了一种简单的方法来限制传入的负载,同时保持线程池和队列的限制。 通常,这允许执行程序“赶上”它正在处理的任务,从而释放队列,池中或两者中的一些容量。 可以从\’executor\’元素上\’rejection-policy\’属性的可用值枚举中选择任何这些选项。
最后,keep-alive设置确定线程在终止之前可以保持空闲的时间限制(以秒为单位)。如果池中当前有多个线程核心数,则在等待这段时间而不处理任务后,多余的线程将被终止。时间值为零将导致多余线程在执行任务后立即终止,而不会在任务队列中保留后续工作。
<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
还有别的一种配置:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 线程池维护线程的最少数量 --> <property name="corePoolSize" value="${taskExecutor.corePoolSize}" /> <!-- 允许的空闲时间 --> <property name="keepAliveSeconds" value="${taskExecutor.keepAliveSeconds}" /> <!-- 线程池维护线程的最大数量 --> <property name="maxPoolSize" value="${taskExecutor.maxPoolSize}" /> <!-- 缓存队列 --> <property name="queueCapacity" value="${taskExecutor.queueCapacity}" /> <!-- 对拒绝task的处理策略 --> <property name="rejectedExecutionHandler"> <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /> <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 --> <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 --> <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 --> <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 --> </property> </bean> <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/> <task:scheduler id="taskScheduler" pool-size="5"/>
属性字段说明
corePoolSize:线程池维护线程的最少数量
keepAliveSeconds:允许的空闲时间
maxPoolSize:线程池维护线程的最大数量
queueCapacity:缓存队列
rejectedExecutionHandler:对拒绝task的处理策略
execute(Runable)方法执行过程
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maxPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maxPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。