计划任务功能在应用程序及其常见,使用Spring Boot的@Scheduled 注解可以很方便的定义一个计划任务。然而在实际开发过程当中还应该注意它的计划任务默认是放在容量为1个线程的线程池中执行,即任务与任务之间是串行执行的。如果没有注意这个问题,开发的应用可能出现不按照设定计划执行的情况。本文将介绍几种增加定时任务线程池的方式。

验证Spring Boot计划任务中的“坑”

其实是验证Spring Boot 中执行任务的线程只有1个。先定义两个任务,输出任务执行时的线程ID,如果ID一样则认为这两个任务是一个线程执行的。这意味着,某个任务即使到达了执行时间,如果还有任务没有执行完,它也不能开始。

package com.example.springbootlearning.schedule;

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; @Component
@EnableScheduling
public class ScheduleDemo { @Scheduled(fixedRate = 5000)
public void work1() throws InterruptedException {
System.out.println(System.currentTimeMillis() + ":" + Thread.currentThread().getId() + ": work1 Begin.");
Thread.sleep(3000L);
System.out.println(System.currentTimeMillis() + ":" + Thread.currentThread().getId() + ": work1 End.");
} @Scheduled(fixedRate = 5000)
public void work2() throws InterruptedException {
System.out.println(System.currentTimeMillis() + ":" + Thread.currentThread().getId() + ": work2 Begin.");
Thread.sleep(3000L);
System.out.println(System.currentTimeMillis() + ":" + Thread.currentThread().getId() + ": work2 End.");
} }

部分输出:

1577949369664:20: work2 Begin.
1577949371665:20: work2 End.
1577949371665:20: work1 Begin.
1577949373665:20: work1 End.
......

以上代码定义了两个任务work1和work2,都是每5000ms执行一次,输出开始执行时间戳,线程ID,和结束时间戳。从输出可以看出,执行两个work的线程是相同的(ID都是20),work2先抢占线程资源,work2 执行结束之后 work1才开始执行。这导致:本来按计划work1应该每5秒执行一次,现在却变成了每6秒执行一次。如图:

让计划任务在不同的线程中执行

要让计划任务在不同的线程中执行,只需要自己去定义执行任务的线程池就可以了。具体操作就是添加一个实现了 SchedulingConfigurer 接口的配置类,然后在配置类里面设置线程池。具体代码如下:

package com.example.springbootlearning.schedule;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import java.util.concurrent.Executors; @Configuration
public class ScheduleConfiguration implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { // 将 Scheduler 设置为容量为 5 的计划任务线程池。
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}

Spring Boot 版本2.1.0之后,可以直接在配置文件中设置 spring.task.scheduling.pool.size 的值来配置计划任务线程池的容量,而不需要额外再写一个类。

spring.task.scheduling.pool.size=5

输出:

1577953149351:20: work2 Begin.
1577953149352:21: work1 Begin.

从输出可以看出,两个任务可以认为是同时开始的(相差1毫秒),分别在ID为20和ID为21的线程中执行。如图:

小结

Spring Boot 定时任务的线程数默认是1,直接使用它调用定时任务会导致不同的任务无法并行执行。因此在使用它之前应该根据需求在application.properties中修改它的线程池容量;或者实现SchedulingConfigurer接口,并在重写的方法中自定义一个线程池。

Spring Boot 计划任务中的一个“坑”的更多相关文章

  1. spring boot: 计划任务@ EnableScheduling和@Scheduled

    spring boot: 计划任务@ EnableScheduling和@Scheduled @Scheduled中的参数说明 @Scheduled(fixedRate=2000):上一次开始执行时间 ...

  2. Spring Boot 2.2 增加了一个新功能,启动飞起~

    前几天栈长分享了一个好玩的框架:一个比Spring Boot快44倍的Java框架!,是不是感觉 Spring Boot 略慢?今天讲一下 Spring Boot 添加的这个新特性,可以大大提升 Sp ...

  3. spring boot 学习(六)spring boot 各版本中使用 log4j2 记录日志

    spring boot 各版本中使用 log4j2 记录日志 前言 Spring Boot中默认日志工具是 logback,只不过我不太喜欢 logback.为了更好支持 spring boot 框架 ...

  4. Spring Boot和Feign中使用Java 8时间日期API(LocalDate等)的序列化问题【转】

    Spring Boot和Feign中使用Java 8时间日期API(LocalDate等)的序列化问题 http://blog.didispace.com/Spring-Boot-And-Feign- ...

  5. Spring Boot源码中模块详解

    Spring Boot源码中模块详解 一.源码 spring boot2.1版本源码地址:https://github.com/spring-projects/spring-boot/tree/2.1 ...

  6. 最详细的 Spring Boot 多模块开发与排坑指南

    创建项目 创建一个 SpringBoot 项目非常的简单,简单到这里根本不用再提.你可以在使用 IDEA 新建项目时直接选择 Spring Initlalize 创建一个 Spring Boot 项目 ...

  7. andriod8.1.0源码编译中的一个坑-package com.sun.javadoc does not exist

    这里记录编译过程中的一个坑!!! 编译过程中出现了下面的报错 external/doclava/src/com/google/doclava/ClassInfo.java:20: error: pac ...

  8. 阿里P7级教你如何在Spring Boot应用程序中使用Redis

    在Spring Boot应用程序中使用Redis缓存的步骤: 1.要获得Redis连接,我们可以使用Lettuce或Jedis客户端库,Spring Boot 2.0启动程序spring-boot-s ...

  9. spring boot开发,jar包一个一个来启动太麻烦了,写一个bat文件一键启动

    spring boot开发,jar包一个一个来启动太麻烦了,写一个bat文件一键启动 @echo offcd D:\workProject\bushustart cmd /c "title ...

随机推荐

  1. PHash从0到1

    背景 在重复图识别领域,对于识别肉眼相同图片,PHash是很有用的,而且算法复杂度很低.它抓住了 " 人眼对于细节信息不是很敏感 " 的特性,利用DCT变换把高频信息去掉,再加上合 ...

  2. hashmap底层:jdk1.8前后的改变

    将hashmap和currenthashmap放一块进行比较,是因为二者的结构相差不多,只不过后者是线程安全的. 首先说hashmap,在jdk1.8之前,hashmap的存储结构是数组+链表的形式, ...

  3. 四、git学习之——分支管理、解决冲突

    分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平行宇宙合并 ...

  4. ftp之filezilla使用记录

    1.550 can't access file错误 我们在客户端查看文件时,有些文件被另外的程序占用了,就会出现这个错误.比如,log日志文件.解决办法:在server端,找到settings设置,找 ...

  5. JavaSE20-线程&同步

    1.线程 1.1 基本概念 线程的概念 线程(Thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并 ...

  6. Maven笔记之面试题合集

    简介:收集整理了网上关于Maven的面试问题,准备面试使用,答案通过各种资料查证编写. 1.什么是Maven? Maven主要服务于基于java平台的项目构建,依赖管理和项目信息管理.Maven项目对 ...

  7. Python科学计算库Numpy

    Python科学计算库Numpy NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. 1.简 ...

  8. ASP.NET Core 3.1使用 AutoMapper

    多层架构中存在多种模型,如视图模型ViewModel,数据传输对你DTO,ORM对象等,这些数据在层与层之间进行传输必须涉及类型之间的转换. AutoMapper是一个对象-对象映射器,作用是通过设置 ...

  9. 解决[BScroll warn]: Can not resolve the wrapper DOM. Vue better-scroll

    在开发项目过程中,使用better-scroll插件中遇到了滚动一次重复提示相同错误 [BScroll warn]: Can not resolve the wrapper DOM. Vue bett ...

  10. 如何对项目中的问题进行分析——FPGA失败案例小结

    本人最近在做一个小项目,自己取名叫做<基于zedboard的千兆以太网底层设计>,一般我都是写好各模块的verilog代码,确定模块没bug后再做整个系统级联,之后直接先进行综合看看有没有 ...