Spring Boot2.0中配置Scheduling定时任务多线程池执行的方法

20 篇文章 0 订阅
8 篇文章 0 订阅

本文章主要介绍了关于Spring Boot 2.0后中配置定时任务、线程池与多线程池。

一、配置基础的定时任务

1.pom.xml添加依赖

<!--定时任务quartz依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

2.Application添加注解@EnableScheduling



import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        System.out.println("start........");
        SpringApplication.run(Application.class, args);
        System.out.println("start........Success");
    }

}

3.创建定时任务
新建类ExpertRecommendCountTask

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * Created by Bruce on 2020/4/23
 **/
@Component
@Configurable
public class ExpertRecommendCountTask {

    private static final Logger logger = LoggerFactory.getLogger(ExpertRecommendCountTask.class);


    /**
     * 每两个小时执行一次
     */
    @Scheduled(cron="0 0 0/2 * * ?")
//    @Scheduled(cron="0/20 * * * * ?")
    public void expertRecommendResultCount() {

        logger.info("【---executor1】定时任务启动.....");
        logger.info("【--executor1】定时任务结束.....");
    }

}

至此配置基础的定时任务已经结束。

二、配置单线程池执行定时任务

1.在配置基础定时任务代码的基础之上添加 定时任务配置类SchedulerConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by Bruce on 2020/4/23
 **/
@Configuration
@EnableAsync
@EnableScheduling
public class SchedulerConfig  {
    /**
     * * 1. 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务,否就选择一条线程执行任务,是就执行第二步。
     *  * 2. 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第三步。
     *  * 3. 查看任务队列是否已满,不满就将任务存储在任务队列中(SynchronousQueue同步队直接执行第四步),否则执行第四步。
     *  * 4. 查看线程池是否已满,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。
     * @return
     */
    @Bean
    public Executor executor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /**
         * 线程名称前缀
         */
        executor.setThreadNamePrefix("executor1-");
        /**
         * //此方法返回可用处理器的虚拟机的最大数量; 不小于1
         */
        int core = Runtime.getRuntime().availableProcessors();

        /**
         * <!-- 最大线程数,默认为Integer.Max_value -->
         *  <property name="maxPoolSize" value="10" />
         */
        executor.setMaxPoolSize(core * 2 + 1);
        /**
         * <!-- 核心线程数,默认为1 -->
         * <property name="corePoolSize" value="3" />
         */
        executor.setCorePoolSize(core);
        /**
         * <!-- 队列最大长度 >=mainExecutor.maxSize -->
         * <property name="queueCapacity" value="25" />
         * //如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
         */
        executor.setQueueCapacity(40);

//        /**
//         * 除核心线程外的线程存活时间
//         */
//        executor.setKeepAliveSeconds(3);
        /**
         * <!-- 线程池对拒绝任务(无线程可用)的处理策略 ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃.  -->
         *         <property name="rejectedExecutionHandler">
         *         <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
         *         <!-- CallerRunsPolicy:若已达到待处理队列长度,将由主线程直接处理请求 -->
         *         <!-- DiscardOldestPolicy:抛弃旧的任务;会导致被丢弃的任务无法再次被执行 -->
         *         <!-- DiscardPolicy:抛弃当前任务;会导致被丢弃的任务无法再次被执行 -->
         *             <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
         *         </property>
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

2.修改定时任务类ExpertRecommendCountTask 中代码
在定时任务方法中添加注解@Async

添加@Async注解,表示该定时任务是异步执行的,因为上面线程池配置了名字,所以可以看到打印的日志是该线程池中的线程在执行任务,如果没有配置线程池的话会默认使用SimpleAsyncTaskExecutor,这个异步执行器每次都会开启一个子线程执行,性能消耗比较大,所以最好是自己配置线程池

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * Created by Bruce on 2020/4/23
 **/
@Component
@Configurable
public class ExpertRecommendCountTask {

    private static final Logger logger = LoggerFactory.getLogger(ExpertRecommendCountTask.class);


    /**
     * 每两个小时执行一次
     */
    @Async
    @Scheduled(cron="0 0 0/2 * * ?")
//    @Scheduled(cron="0/20 * * * * ?")
    public void expertRecommendResultCount() {

        logger.info("【---executor1】定时任务启动.....");
        logger.info("【--executor1】定时任务结束.....");
    }

}

至此配置单线程池执行定时任务执行完毕

三、配置多线程池执行定时任务

1.修改SchedulerConfig类,在原线程池‘executor1’基础之上,增加新的线程池‘executor2’。


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by Bruce on 2020/4/23
 **/
@Configuration
@EnableAsync
@EnableScheduling
public class SchedulerConfig  {
    

    /**
     * * 1. 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务,否就选择一条线程执行任务,是就执行第二步。
     *  * 2. 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第三步。
     *  * 3. 查看任务队列是否已满,不满就将任务存储在任务队列中(SynchronousQueue同步队直接执行第四步),否则执行第四步。
     *  * 4. 查看线程池是否已满,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。
     * @return
     */
    @Bean
    public Executor executor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /**
         * 线程名称前缀
         */
        executor.setThreadNamePrefix("executor1-");
        /**
         * //此方法返回可用处理器的虚拟机的最大数量; 不小于1
         */
        int core = Runtime.getRuntime().availableProcessors();

        /**
         * <!-- 最大线程数,默认为Integer.Max_value -->
         *  <property name="maxPoolSize" value="10" />
         */
        executor.setMaxPoolSize(core * 2 + 1);
        /**
         * <!-- 核心线程数,默认为1 -->
         * <property name="corePoolSize" value="3" />
         */
        executor.setCorePoolSize(core);
        /**
         * <!-- 队列最大长度 >=mainExecutor.maxSize -->
         * <property name="queueCapacity" value="25" />
         * //如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
         */
        executor.setQueueCapacity(40);

//        /**
//         * 除核心线程外的线程存活时间
//         */
//        executor.setKeepAliveSeconds(3);
        /**
         * <!-- 线程池对拒绝任务(无线程可用)的处理策略 ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃.  -->
         *         <property name="rejectedExecutionHandler">
         *         <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
         *         <!-- CallerRunsPolicy:若已达到待处理队列长度,将由主线程直接处理请求 -->
         *         <!-- DiscardOldestPolicy:抛弃旧的任务;会导致被丢弃的任务无法再次被执行 -->
         *         <!-- DiscardPolicy:抛弃当前任务;会导致被丢弃的任务无法再次被执行 -->
         *             <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
         *         </property>
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    @Bean
    public Executor executor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("executor2-");
        executor.setMaxPoolSize(20);
        executor.setCorePoolSize(5);
        executor.setQueueCapacity(0);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

}

2.修改ExpertRecommendCountTask类
在ExpertRecommendCountTask类中新增定时任务‘expertRecommendCount’,并指定线程池

修改原定时任务方法‘expertRecommendResultCount’上注解‘@Async’为‘@Async(“executor1”)’,指定使用上一步配置的线程池‘executor1’。
新增定时任务方法‘expertRecommendCount’,添加注解‘@Async(“executor2”)’,指定使用上一步配置的线程池‘executor2’。

因为上面在配置类里面初始化了两个线程池,所以会有两个线程池分别叫executor1和executor2被生成放到容器中,因为@Bean注解生成的对象默认就是和方法名相同的名字,而@Async注解是可以指定使用哪个线程池的。这样就可以在不同的线程池中执行不同的定时任务了。
注意:
①.没有配置自己的线程池时,会默认使用SimpleAsyncTaskExecutor。
②.如果项目中只配置了一个线程池,那么不需要显示指定使用这个线程池,spring也会自动使用用户配置的线程池,但是如果配置了多个就必须要显示指定,否则还是会使用默认的。
③.如果想要指定使用哪个线程池,可以使用@Async(“executor2”)显示指定。


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * Created by Bruce on 2020/4/23
 **/
@Component
@Configurable
public class ExpertRecommendCountTask {

    private static final Logger logger = LoggerFactory.getLogger(ExpertRecommendCountTask.class);

 



    /**
     * 每两个小时执行一次
     */
    @Async("executor1")
    @Scheduled(cron="0 0 0/2 * * ?")
//    @Scheduled(cron="0/20 * * * * ?")
    public void expertRecommendResultCount() {

        logger.info("【---executor1】定时任务启动.....");
        logger.info("【---executor1】定时任务结束.....");
    }


    /**
     * 每天的1点、13点、15点、17点、19点、21点、23点都执行一次:0 0 1,13,15,17,19,21,23 * * ?
     */
    @Async("executor2")
    @Scheduled(cron="0 0 1,13,15,17,19,21,23 * * ?")
//    @Scheduled(cron="0/20 * * * * ?")
    public void expertRecommendCount() {

        logger.info("【---executor2】定时任务启动.....");
        logger.info("【---executor2】定时任务结束.....");
    }
}

至此配置多线程池已经完毕。

以下是使用线程池需要注意的事项:

 int corePoolSize:线程池维护线程的最小数量.    
 int maximumPoolSize:线程池维护线程的最大数量.    
 long keepAliveTime:空闲线程的存活时间.    
 TimeUnit unit: 时间单位,现有纳秒,微秒,毫秒,秒枚举值.    
 BlockingQueue<Runnable> workQueue:持有等待执行的任务队列.    
 RejectedExecutionHandler handler:    
 用来拒绝一个任务的执行,有两种情况会发生这种情况。    
  一是在execute方法中若addIfUnderMaximumPoolSize(command)为false,即线程池已经饱和;    
 二是在execute方法中, 发现runState!=RUNNING || poolSize == 0,即已经shutdown,就调用ensureQueuedTaskHandled(Runnable command),在该方法中有可能调用reject。
 
  ThreadPoolExecutor池子的处理流程如下:  
 1)当池子大小小于corePoolSize就新建线程,并处理请求
  2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理
  3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理
 4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁
  其会优先创建  CorePoolSiz 线程, 当继续增加线程时,先放入Queue中,当 CorePoolSiz  和 Queue 都满的时候,就增加创建新线程,当线程达到MaxPoolSize的时候,就会抛出错 误 org.springframework.core.task.TaskRejectedException
  另外MaxPoolSize的设定如果比系统支持的线程数还要大时,会抛出java.lang.OutOfMemoryError: unable to create new native thread 异常。
 
 
  **Reject拒绝策略预定义有四种:**
 (1)ThreadPoolExecutor.AbortPolicy策略,是默认的策略,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
 (2)ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃.
 (3)ThreadPoolExecutor.DiscardPolicy策略,不能执行的任务将被丢弃.
 (4)ThreadPoolExecutor.DiscardOldestPolicy策略,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程

参考:
JAVA线程池学习,ThreadPoolTaskExecutor和ThreadPoolExecutor有何区别?

Spring Boot中配置定时任务、线程池与多线程池执行的方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值