SpringBoot——@Scheduled的自定义周期性线程池解决任务延时执行问题

831 阅读2分钟

关注微信公众号:CodingTechWork,一起学习进步。

问题

  在使用Spring中的@Scheduled注解设置定时任务时,遇到这样2个问题:

  1. 定时任务未按时执行,现象是延后了一段时间才执行定时任务。
  2. 多个定时任务有时间重叠时,无法并发调度执行。

分析

  出现上面问题现象的根因是Spring的定时任务默认是单线程执行,所以会在某些场景下造成阻塞。当然我们可以通过@Async注解来异步执行这些并发的@Scheduled注解的定时任务,而@Async线程池容量是100,当超过100个线程并发执行时,则剩下的定时任务会等待之前的线程释放,不会自行扩容。   既然@Async是个定值大小的线程池,还是有出现定时任务延时执行的问题,所以下面我们可以通过其他方式来自定义线程池大小。

解决方式

  通过自定义配置线程池来解决问题。

package com.andya.selfcode.conf;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.*;



/**
 * @author Andya
 * @create 2021-03-31
 */
@Slf4j
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {

	//自定义线程池大小,可配置。代码中默认10个
    @Value("${threadPool.schedule.coreSize: 10}")
    public int SCHEDULE_CORE_SIZE;

	//自定义线程池名称
	public static final String THREAD_NAME_WITH_SCHEDULE = "schedule-thread-%d";
	
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//        scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(SCHEDULE_CORE_SIZE));
        scheduledTaskRegistrar.setScheduler(this.buildSchedulerThreadPool());
        System.out.println("Scheduler threadpool core_size: " + SCHEDULE_CORE_SIZE);
    }

    /**
     * Spring的@Scheduled的自定义周期性线程池
     * @return
     */
    @Bean(value = "scheduleThreadPool")
    public ExecutorService buildSchedulerThreadPool() {
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat(THREAD_NAME_WITH_SCHEDULE).build();

        /**
         * 1. CallerRunsPolicy :    这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
         2. AbortPolicy :         对拒绝任务抛弃处理,并且抛出异常。
         3. DiscardPolicy :       对拒绝任务直接无声抛弃,没有异常信息。
         4. DiscardOldestPolicy : 对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。
         不写则为默认的AbortPolicy策略。
         */
        ScheduledExecutorService threadPool = new ScheduledThreadPoolExecutor(
                SCHEDULE_CORE_SIZE,
                threadFactory);
        return threadPool;
    }
}