关于java:SpringBoot定时任务和异步操作

66次阅读

共计 3830 个字符,预计需要花费 10 分钟才能阅读完成。

SpringBoot 定时工作和异步操作

日常求赞,感激老板。

欢送关注公众号:其实是白羊。干货继续更新中 ……

一、定时工作

在做业务时总会有这样的场景:在特定工夫去执行某些逻辑。这其实就是定时工作的利用场景,比方:须要每月一日给用户发上月数据总结等场景。

1. 技术

实现定时工作的技术很多

  • Timer:JDK 自带的 java.util.Timer 其实更相似于定时器,可实现提早执行和依照肯定频率执行,也能够指定某个工夫执行,应用较少
  • ScheduledExecutorService:也是 JDK 自带的,是基于线程池设计的定时工作类,依据 Executors 创立时的线程数量去执行具体任务(多个线程数量就是每个人物调配一个线程)
  • Spring Task:即明天要介绍了配角,是 Spring 自带的,当然这里通过 Springboot 应用。
  • Quartz:开源的调度框架,性能更加弱小

2. 注解应用

1)@EnableScheduling

开启定时工作,可标注在启动类或者任何配置类上(能扫描到对象上都能够)

2)@Scheduled

配置具体的工作执行规定,可标注在能被扫描的类的办法上,属性有:

  • fixedRate:上一个工作开始工夫和下一个工作开始工夫的工夫距离(毫秒)
  • fixedDelay:上一个工作的完结工夫和下一个工作的开始工夫的工夫距离(毫秒)
  • initialDelay:第一次执行提早执行的工夫(毫秒)
  • cron:通过 cron 表达式来配置执行机会

3.cron 表达式

定时工作的场景:提早执行、肯定频率执行、指定工夫执行

这里的 cron 表达式即为了形容工作执行的工夫规定。cron 由 6 - 7 个元素组成,他们之间应用空格来宰割,以此代表:

  • 秒:0~59
  • 分:0~59
  • 时:0~23
  • 日:1~31(具体月的最初一天也可能是 30)
  • 月:1~12
  • 星期:1~7(留神 1 为周日)
  • 年:1970~2099

除了下面的数字元素值还有上面几个非凡的元素值:

  • *:示意任意一个值都会触发,七个元素地位中都能够呈现,如在分钟呈现即示意每分钟都回去执行
  • ?:和 * 相似,但只会呈现在日和星期(这两个地位只能有一个是具备意义的形容,如:要么是每个月的 3 号要么是每个月的每个星期的周三),当其中一个配置了有意义的值,另一个写?
  • -:示意范畴(至),七个元素地位中都能够呈现,如分钟里呈现 1 - 7 则示意 1 分钟到 7 分钟每分钟执行一次
  • /:示意从开始工夫每隔多长时间执行一次,七个元素地位中都能够呈现,如分钟里呈现 1 / 7 则示意 1 分触发一次 1 +7= 8 分触发一次
  • ,:示意枚举值,七个元素地位中都能够呈现,如分钟里呈现 1,7 则示意 1 分和 7 分各执行一次
  • L:示意最初,只会呈现在日和星期地位,日示意最初一天,星期则会搭配数字如 5L 示意最初一个周四
  • W:示意无效工作日(周一到周五),只会呈现在日期里,后面会搭配数字,如 5W:如果 5 号是周六那么执行工夫为周五即 4 号;如果 5 号是周日那么执行工夫为下周一即 6 号;(即零碎会举荐离明天最近的一个工作日,但留神不会进行逾越查找:如果是 31W 且 31 号是周日,那么会在 29 号执行)
  • LW:连用示意最初一个工作日,只会呈现在日期里
  • “#”:(前后要加数字)示意第几个星期几,只会呈现在星期里,如 4#2 即示意第二个周三(后面示意周几,前面示意第几个)

理解了下面的表达式规定就能够写出满足条件的 cron 表达式了,倡议多设计几个场景练习下

4. 单线程和多线程执行

如果就按下面的配置写好工作 A(fixedRate=2000)和工作 B(fixedRate=3000)间接启动执行的话,会存在以下问题:

  • 如果 A1 工作执行工夫超过 2s 那么原定于 A1 工作开始时 2s 后执行的工作 A2 就不能按时执行
  • 工作 A 和工作 B 要交替执行

下面的问题都不能让咱们的工作依照各自规定的执行打算去执行,归根到底还是所有的工作都在一个线程里进行,所以做不到异步的并发执行,那这是就能够通过多线程来实现各个工作之间异步的并发执行

这里咱们能够应用 Spring 的 @Async 注解来实现异步

二、异步

SpringBoot 同样反对 @Async 来实现异步(减少了主动配置可间接应用)

1. 注解

1)@EnableAsync

标注在启动类或配置类上,表是开启异步

2)@Async

可标注在类上(能被 spring 容器扫描到的类上)或办法上

标注在类上则这个类里的办法都被示意为异步

2. 异步办法两种返回值

  1. 不须要返回值:void
  2. 须要返回值:

     @Async  
        public Future<String> doTaskOne() throws Exception {return new AsyncResult<>("工作一实现");  
        }  

3. 主动配置

应用 @Async 来实现异步,其底层还是应用了多线程来进行实现的,那么这个多线程或者线程池是在哪里设置或配置的呢?咱们都直到 SpringBoot 退出了大量的主动配置,咱们在 spring-boot-autoconfigure 包上面能够找到 task 包下的 TaskExecutionAutoConfiguration 类中:

@Lazy
@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
@ConditionalOnMissingBean(Executor.class)
// 这个注解的意思:当在容器中没有发现 Executor 这个类则会加载这个 bean,能够了解为此处为默认缺省对象
// 返回的是一个 spring 为咱们提供的线程池
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {return builder.build();
    }

找到 TaskExecutorBuilder:

@Bean
    @ConditionalOnMissingBean
    public TaskExecutorBuilder taskExecutorBuilder() {TaskExecutionProperties.Pool pool = this.properties.getPool();
        TaskExecutorBuilder builder = new TaskExecutorBuilder();
        builder = builder.queueCapacity(pool.getQueueCapacity());
        builder = builder.corePoolSize(pool.getCoreSize());
        builder = builder.maxPoolSize(pool.getMaxSize());
        builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
        builder = builder.keepAlive(pool.getKeepAlive());
        builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
        builder = builder.customizers(this.taskExecutorCustomizers.orderedStream()::iterator);
        builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
        return builder;
    }

这里的 properties 是在 TaskExecutionProperties 加载进来的,默认的参数:

private int queueCapacity = Integer.MAX_VALUE;
private int coreSize = 8; 
private int maxSize = Integer.MAX_VALUE;
private boolean allowCoreThreadTimeout = true;

依据上面可知,也能够从 yml/properties 文件中自定义配置

@ConfigurationProperties("spring.task.execution")

除了下面的办法咱们还能够定义本人的 Bean 来替换默认缺省 Bean,依据 @ConditionalOnMissingBean(Executor.class) 可知,咱们只须要在配置类里加上:

@Bean
    public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(外围线程数量);
        executor.setMaxPoolSize(最大线程数量);
        executor.setQueueCapacity(工作队列大小);
        executor.initialize();
        return executor;
    }

三、最初

依据下面的介绍,要实现异步的定时工作就能够开展,工作办法下面加上 @Async 来实现

点个赞啊亲

如果你认为本文对你有帮忙,能够「在看 / 转发 / 赞 /star」,多谢

如果你还发现了更好或不同的想法,还能够在留言区一起探讨下


欢送关注公众号:「其实是白羊」干货继续更新中 ……

正文完
 0