首先看源码注释
/**
* Annotation that marks a method to be scheduled. Exactly one of the
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes must be
* specified.
*
* 将一个方法标记为定时任务的注解。必须明确地指定cron,fixedDelay或fixedRate属性
*
*
* <p>The annotated method must expect no arguments. It will typically have
* a {@code void} return type; if not, the returned value will be ignored
* when called through the scheduler.
*
* 带注解的方法不能有参数。返回值类型必须是void;否则,调度器调用时会忽略返回值。
*
*
* <p>Processing of {@code @Scheduled} annotations is performed by
* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be
* done manually or, more conveniently, through the {@code <task:annotation-driven/>}
* element or @{@link EnableScheduling} annotation.
*
* 通过注册到ScheduledAnnotationBeanPostProcessor后处理bean中来执行注解标注的程序。可以手动将定时任务注册到ScheduledAnnotationBeanPostProcessor后处理bean中,或者通过<task:annotation-driven/>标签和@EnableScheduling注解。
*
*
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
* <em>composed annotations</em> with attribute overrides.
*
* 看不懂(可以作为元注解创建自定义注解?)
*
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Dave Syer
* @author Chris Beams
* @since 3.0
* @see EnableScheduling
* @see ScheduledAnnotationBeanPostProcessor
* @see Schedules
*/
注释里说@Scheduled注解的方法被注册到ScheduledAnnotationBeanPostProcessor后处理Bean里面了,来看看ScheduledAnnotationBeanPostProcessor源码
public class ScheduledAnnotationBeanPostProcessor
implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
/**
* The default name of the {@link TaskScheduler} bean to pick up: {@value}.
* <p>Note that the initial lookup happens by type; this is just the fallback
* in case of multiple scheduler beans found in the context.
* @since 4.2
*/
public static final String DEFAULT_TASK_SCHEDULER_BEAN_NAME = "taskScheduler";
private final ScheduledTaskRegistrar registrar;
@Nullable
private Object scheduler;
/**
* Create a default {@code ScheduledAnnotationBeanPostProcessor}.
*/
public ScheduledAnnotationBeanPostProcessor() {
this.registrar = new ScheduledTaskRegistrar();
}
/**
* Create a {@code ScheduledAnnotationBeanPostProcessor} delegating to the
* specified {@link ScheduledTaskRegistrar}.
* @param registrar the ScheduledTaskRegistrar to register @Scheduled tasks on
* @since 5.1
*/
public ScheduledAnnotationBeanPostProcessor(ScheduledTaskRegistrar registrar) {
Assert.notNull(registrar, "ScheduledTaskRegistrar is required");
this.registrar = registrar;
}
/**
* Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
* the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
* to be wrapped as a TaskScheduler.
*
* 设置被调用的方法为TaskScheduler,或将ScheduledExecutorService包装成TaskScheduler。
*
*
* <p>If not specified, default scheduler resolution will apply: searching for a
* unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler}
* bean named "taskScheduler" otherwise; the same lookup will also be performed for
* a {@link ScheduledExecutorService} bean. If neither of the two is resolvable,
* a local single-threaded default scheduler will be created within the registrar.
* @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME
*
* 设置被调用的方法为TaskScheduler,或将ScheduledExecutorService包装成TaskScheduler。
* 如果没有指定,将使用以下两个默认方案:
* 一、在容器里查找唯一的TaskScheduler实例。
* 二、在容器里查找名字是taskScheduler的实例。
* 如果两个方案都无法解析,将会创建一个单线程默认调度器。
*
* ???
*
*/
public void setScheduler(Object scheduler) {
this.scheduler = scheduler;
}
}
意味着, 如果不配置TaskScheduler
, SpringBoot会默认使用一个单线程的scheduler来处理@Scheduled
注解标注的定时任务。
发现项目中有一个定时任务延迟6秒启动,然后死循环。(@Scheduled注解各参数详解)
/**
* 延迟60秒继续执行,异步执行;
*/
@Scheduled(fixedDelay = 6000)
public void run() {
log.info("init zk register");
while (true) {
log.info("do something...");
Thread.sleep(5000);
}
}
由此断定是因为线程池里唯一的线程被死循环占用了。
解决方案:
一:注册zk的线程修改为5秒执行一次的定时任务,不睡眠。
/**
* 延迟60秒继续执行,异步执行;
*/
@Scheduled(cron = "0/5 * * * * ?")
public void run() {
log.info("init zk register");
log.info("do something...");
}
二:主动配置TaskScheduler,指定线程池线程数量。
package com.soyoung.ztdata.dataservice.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
/**
* @program: zt_service
* @description:
* @author: Leon
* @create: 2021-06-04 22:07
**/
@Configuration
public class SoYongSchedulingConfiguration implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(2);
threadPoolTaskScheduler.initialize();
taskRegistrar.setScheduler(threadPoolTaskScheduler);
}
}
三:修改配置文件,指定定时任务线程池线程数量。
spring.task.scheduling.pool.size=2