Regarding the problem that @Scheduled scheduled tasks can only execute one task at a time

Today, when I was doing a scheduled task, I encountered the problem that several scheduled task methods should be run at the same time, but during the running process, I found that only one task can be executed when there are multiple tasks, and other tasks need to wait for this task to be executed before continuing. , After a lot of Baidu and research, I found the problem and the solution.

1. Test

I created a test class for the next test.

@EnableScheduling
@SpringBootApplication
public class JobApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(JobApplication.class,args);
    }
}
@Component
@Slf4j
public class ExecuteTaskJob {
    
    
    /**
     * 执行除定时存储任务外的所有定时任务(异步执行)
     */
    @Scheduled(cron = "* * * * * ? ")
    public void executeTask() throws InterruptedException {
    
    
        //获取除定时存储任务外的定时任务,包括历史任务
        System.err.println("测试执行了---------------" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(3);
    }

    @Scheduled(cron = "* * * * * ? ")
    public void test() throws InterruptedException {
    
    
        //获取除定时存储任务外的定时任务,包括历史任务
        System.err.println("测试任务测试执行了---------------" + Thread.currentThread().getName());
    }
}

After executing the test, I found that the printed thread names are all the same. It should be executed by different threads, so there is a problem.

测试任务测试执行了---------------scheduling-1
测试执行了---------------scheduling-1
测试任务测试执行了---------------scheduling-1
测试执行了---------------scheduling-1

Let me explore with this question.

2. Explore why timing tasks are not executed by multiple threads

Let's turn on the @EnableScheduling annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
    
    

}

After we opened the EnableScheduling annotation, we found that he introduced the SchedulingConfiguration class, and we entered the SchedulingConfiguration class to take a look.

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
    
    

	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
    
    
		return new ScheduledAnnotationBeanPostProcessor();
	}

}

You can see that he created a ScheduledAnnotationBeanPostProcessor object, let's take a look at the ScheduledAnnotationBeanPostProcessor object.

public ScheduledAnnotationBeanPostProcessor() {
    
    
		this.registrar = new ScheduledTaskRegistrar();
	}

After clicking in, a ScheduledTaskRegistrar class is created and assigned, and we click into the ScheduledTaskRegistrar class.

public class ScheduledTaskRegistrar implements ScheduledTaskHolder, InitializingBean, DisposableBean {
    
    

Click in and it implements many interfaces, take a look at the InitializingBean interface.

public interface InitializingBean {
    
    
	void afterPropertiesSet() throws Exception;
}

This method in the interface is implemented in ScheduledTaskRegistrar, let's find it.

@Override
	public void afterPropertiesSet() {
    
    
		scheduleTasks();
	}

You can see that he called a scheduleTasks method, let's click in.

	@SuppressWarnings("deprecation")
	protected void scheduleTasks() {
    
    
		if (this.taskScheduler == null) {
    
    
			this.localExecutor = Executors.newSingleThreadScheduledExecutor();
			this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
		}
		if (this.triggerTasks != null) {
    
    
			for (TriggerTask task : this.triggerTasks) {
    
    
				addScheduledTask(scheduleTriggerTask(task));
			}
		}
		if (this.cronTasks != null) {
    
    
			for (CronTask task : this.cronTasks) {
    
    
				addScheduledTask(scheduleCronTask(task));
			}
		}
		if (this.fixedRateTasks != null) {
    
    
			for (IntervalTask task : this.fixedRateTasks) {
    
    
				addScheduledTask(scheduleFixedRateTask(task));
			}
		}
		if (this.fixedDelayTasks != null) {
    
    
			for (IntervalTask task : this.fixedDelayTasks) {
    
    
				addScheduledTask(scheduleFixedDelayTask(task));
			}
		}
	}

The most important thing here is that he judges that if the taskScheduler is empty, it will create a thread pool for scheduled tasks by default, and call this method Executors.newSingleThreadScheduledExecutor(), let's click in and have a look.

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    
    
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

I found that he passed in a 1 when creating the thread pool of the scheduled task. This 1 means that the number of threads is 1. If you don’t understand, you can click a little more in it, and then you will learn how to create a thread pool. More narration.

In fact, it is clear here that the thread pool created when the scheduled task is implemented internally is single, so it will cause blocking, so let's talk about the solution, we can create multiple thread pools for him.

3. Solve the problem of timing task blocking

We create a thread pool for scheduled tasks in the startup class and specify our own number of threads.

@EnableScheduling
@SpringBootApplication
public class JobApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(JobApplication.class,args);
    }

    /**
     * 因为定时器默认创建的线程池为一个线程的所以这里需要改成多个线程的,不然相同时间段任务执行时会等待,因为只有一个线程在工作
     * @return
     */
    @Bean
    public TaskScheduler taskScheduler() {
    
    
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

So far, the problem that only one scheduled task can be executed at the same time is solved, let's test it again.

测试任务测试执行了---------------taskScheduler-4
测试任务测试执行了---------------taskScheduler-6
测试任务测试执行了---------------taskScheduler-1
测试任务测试执行了---------------taskScheduler-5
测试执行了---------------taskScheduler-4
测试任务测试执行了---------------taskScheduler-5
测试任务测试执行了---------------taskScheduler-5
测试任务测试执行了---------------taskScheduler-1
测试任务测试执行了---------------taskScheduler-8
测试执行了---------------taskScheduler-4

It can be seen that the thread name is no longer a thread executing tasks when it is executed now, which proves that the problem has been solved.

Guess you like

Origin blog.csdn.net/qq_45699784/article/details/130887726