SpringBoot-Schedule多线程详解

Scroll Down

SpringBoot Schedule详解

本文参考链接,部分内容来自于该链接:SpringBoot Schedule设置

在开发过程中在一个微服务中要调用多个定时同步的方法, 发现Sprint官方默认开启的Schedul是单线程的,然后第二个定时任务要等到第一个定时跑完才可以开始跑,那么严重影响效率有木有,随手搜了一下可以通过配置线程池和@Async来解决。因此开始配置线程池。

SpringBoot Schedule多线程

线程池配置

@Configuration
@EnableAsync
public class SpringAsyncConfig{
    @Bean
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数量
        executor.setCorePoolSize(20);
        //线程池维护的线程的最大数量
        executor.setMaxPoolSize(1000);
        //缓冲队列
        executor.setQueueCapacity(100);
        //超出核心线程数外的线程在空闲时候的最大存活时间
        executor.setKeepAliveSeconds(300);
        //线程名前缀
        executor.setThreadNamePrefix("asyncThread");
        //是否等待所有线程执行完毕才关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        //当线程没可用线程时的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

在service层添加异步支持
业务方面的service也要用多线程来跑

public interface DemoAsyncService {
    @Async("threadPoolTaskExecutor")
    void demoMethod1(String args...);

    @Async("threadPoolTaskExecutor")
    void demoMethod2(String args...);
}

schedule添加异步支持

    @Async
    @Scheduled(fixedDelay = 1000 * 60 * 60)
    void diagResourceRunner() throws InterruptedException {
        log.info("process resource runner...");
        Thread.sleep(1000 * 5);
        log.info("process resource runner complete");
    }
    @Async
    @Scheduled(fixedDelay = 1000 * 60 * 60)
    void diagAccountRunner() throws InterruptedException {
        log.info("process account runner...");
        Thread.sleep(1000 * 5);
        log.info("process account runner complete");
    }

异步支持结果输出

2020-04-10 11:56:43.128  INFO 3622 --- [TaskScheduler-1] .s.a.AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [threadPoolTaskExecutor, catalogWatchTaskScheduler]
2020-04-10 11:46:42.742  INFO 3599 --- [cTaskExecutor-1] c.a.smartops.services.ScheduledService   : process resource runner...
2020-04-10 11:46:42.742  INFO 3599 --- [cTaskExecutor-2] c.a.smartops.services.ScheduledService   : process account runner...
2020-04-10 11:46:45.105  INFO 3599 --- [on(1)-127.0.0.1] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2020-04-10 11:46:45.107  INFO 3599 --- [on(1)-127.0.0.1] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2020-04-10 11:46:47.745  INFO 3599 --- [cTaskExecutor-1] c.a.smartops.services.ScheduledService   : process resource runner complete
2020-04-10 11:46:47.745  INFO 3599 --- [cTaskExecutor-2] c.a.smartops.services.ScheduledService   : process account runner complete
2020-04-10 11:46:55.629  INFO 3599 --- [       Thread-1] com.anchnet.smartops.Application         : Application shutting down

service不添加异步支持

//    @Async
    @Scheduled(fixedDelay = 1000 * 60 * 60)
    void diagResourceRunner() throws InterruptedException {
        log.info("process resource runner...");
        Thread.sleep(1000 * 5);
        log.info("process resource runner complete");
    }
//    @Async
    @Scheduled(fixedDelay = 1000 * 60 * 60)
    void diagAccountRunner() throws InterruptedException {
        log.info("process account runner...");
        Thread.sleep(1000 * 5);
        log.info("process account runner complete");
    }

不添加异步支持的输出结果

2020-04-10 11:49:18.194  INFO 3606 --- [TaskScheduler-1] c.a.smartops.services.ScheduledService   : process resource runner...
- [  restartedMain] com.anchnet.smartops.Application         : Started Application in 16.697 seconds (JVM running for 18.222)
2020-04-10 11:49:20.758  INFO 3606 --- [on(2)-127.0.0.1] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2020-04-10 11:49:20.760  INFO 3606 --- [on(2)-127.0.0.1] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2020-04-10 11:49:23.202  INFO 3606 --- [TaskScheduler-1] c.a.smartops.services.ScheduledService   : process resource runner complete
2020-04-10 11:49:23.207  INFO 3606 --- [TaskScheduler-1] c.a.smartops.services.ScheduledService   : process account runner...
2020-04-10 11:49:28.209  INFO 3606 --- [TaskScheduler-1] c.a.smartops.services.ScheduledService   : process account runner complete

通过结果我们可以看到,在添加了@Async,schedule会有多个线程,去掉@Async则变成了单线程,至此我的需求已经达到了。但是我们真正在项目开发中并不会只有schedule这样的多线程场景,还会有业务场景也是多线程,例如DemoAsyncService这样的,那么相当于定时任务和demoAsyncService是共用一个线程池,但是有时候我们不希望这样。我还是想要他俩个分开,各做各的事情。而且还有一点在我们使用Async的时候log有一句话,说是context中多个TaskExecutor的bean,但是没有name=taskExecutor的,然后让我们指定一个,这个看的我就很不爽,也很疑惑为啥会这样。

- [TaskScheduler-1] .s.a.AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [threadPoolTaskExecutor, catalogWatchTaskScheduler]

揭开SpringBoot的Schedule

待更新...(着急的朋友可以看文章开始部分的引用链接)