使用
很方便的使用方法是用@Scheduled
注解,在Spring配置文件中加入相应的配置。
其中ThreadPoolTaskScheduler
的poolSize
属性代表用于执行定时任务的线程数。例如有两个定时任务触发的时间相同,如果只有一个线程,那么有一个任务需要等到另一个任务执行完了才能执行,如果线程数是2则可以并行执行。
<task:annotation-driven executor="asyncTaskExecutor" scheduler="scheduledTaskExecutor"/>
<bean id="scheduledTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="${scheduledTaskExecutor.poolSize}"/>
</bean>
源码解读
ScheduledAnnotationBeanPostProcessor
这个类会解析@Schedule注解的方法,获取方法检查是否没有参数,然后包装成ScheduledMethodRunnable
,根据时间的配置是cron
还是fixedDelay
还是fixedRate
,放进这个processor的一个Tasks的map中,这是三个以Rannable为key,时间配置为value的map.
private final Map<Runnable, String> cronTasks = new HashMap();
private final Map<Runnable, Long> fixedDelayTasks = new HashMap();
private final Map<Runnable, Long> fixedRateTasks = new HashMap();
这个类实现了ApplicationListener<ContextRefreshedEvent>
,监听ContextRefreshedEvent事件,当有ApplicationContext refresh完成之后,如果上文提到的三个map至少有一个不为空,则新建一个ScheduledTaskRegistrar
,把三个map set进去,scheduler
也set进去(这个scheduler应该就是在task:annotation-driven配置的)。
如果没有配置scheduler则会取applicationContext中所有的type是TaskScheduler
和ScheduledExecutorService
的bean(只能有一个,否则会报错)。
最后调用registrar.afterPropertiesSet()
实际是把上文中三个map里的task作为参数调用taskScheduler.schedule()
,结果放入private final Set<ScheduledFuture<?>> scheduledFutures = new LinkedHashSet();
中。
ThreadPoolTaskScheduler
Spring的ThreadPoolTaskScheduler类的hierarchy结构如图
这个对象在初始化时会生成一个ScheduledThreadPoolExecutor
作为schedulerExecutor,这个事cunrrent包中的类。 核心线程池大小就是我们配置的poolSize属性,最大线程池大小是Integer.MAX_VALUE
,keepAliveTime
为0,队列是DelayedWorkQueue
,这个队列有一个属性private final DelayQueue<RunnableScheduledFuture> dq = new DelayQueue<RunnableScheduledFuture>();
对这个队列的操作实际是是对这个DelayQueue的操作,这个队列大小是Integer.MAX_VALUE
。
schedule方法实际是调用的ScheduledThreadPoolExecutor
对象的schedule方法。
非周期性任务:
public ScheduledFuture schedule(Runnable task, Date startTime) {
ScheduledExecutorService executor =