为什么Spring Boot 定时任务是单线程的?
想要解释为什么,一定要从源码入手,直接从@EnableScheduling这个注解入手,找到了这个ScheduledTaskRegistrar类,其中有一段代码如下:
1 2 3 4 5 6
| protected void scheduleTasks() { if (this.taskScheduler == null) { this.localExecutor = Executors.newSingleThreadScheduledExecutor(); this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor); } }
|
如果taskScheduler为null,则创建单线程的线程池:Executors.newSingleThreadScheduledExecutor()
。
也就是说线程调度器设置只有一个线程容量,如果存在多个任务被触发时,会等第一个任务执行完毕才会执行下一个任务。
比如下面的demo,两个线程都是每秒执行一次,但是A任务每次执行时都会睡眠10秒,则B任务就变成了每10秒执行一次,因为它要等A任务执行完毕后才能执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Component public class SchedulingTask {
@Scheduled(fixedDelay = 1000) private void a() { try { Thread.sleep(10000); System.out.println("aaa"); }catch (Exception e) { e.printStackTrace(); } }
@Scheduled(fixedDelay = 1000) private void b() { System.out.println("bbb"); } }
|
多线程定时任务如何配置?
下面介绍三种方案配置多线程下的定时任务。
直接实现SchedulingConfigurer这个接口,设置taskScheduler,代码如下:
1 2 3 4 5 6 7 8 9 10
| @Configuration @EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10)); } }
|
2、通过配置开启
SpringBoot 已经提供了一个配置用来配置线程池的大小,如下;
1 2 3 4 5 6 7 8
| spring.task.scheduling.pool.size=1
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.shutdown.await-termination=
spring.task.scheduling.shutdown.await-termination-period=
|
只需要在配置文件中添加如上的配置即可生效!
3、结合@Async
@Async这个注解都用过,用来开启异步任务的,使用@Async这个注解之前一定是要先配置线程池的,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor(); poolTaskExecutor.setCorePoolSize(4); poolTaskExecutor.setMaxPoolSize(6); poolTaskExecutor.setKeepAliveSeconds(120); poolTaskExecutor.setQueueCapacity(40); poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); return poolTaskExecutor; }
|
然后在@Scheduled方法上标注@Async
这个注解即可实现多线程定时任务,代码如下:
1 2 3 4 5
| @Async @Scheduled(cron = "0/2 * * * * ? ") public void test() { System.out.println("..................执行test2................."); }
|