计划任务功能在应用程序及其常见,使用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. Kubernetes 教程:在 Containerd 容器中使用 GPU

    原文链接:https://fuckcloudnative.io/posts/add-nvidia-gpu-support-to-k8s-with-containerd/ 前两天闹得沸沸扬扬的事件不知道 ...

  2. OI学习过程记录

    这帖子本来是教练为了给低年级学生分享经验而让我写的学习经历,不过等我退役之后可能就变成回忆录了. 初三 WC 前:上了正睿的线上课程,练了一些模拟赛,同时也正在学文化课. 然后,莫名奇妙1膜考了全校前 ...

  3. 协程gevent学习

    import gevent def f1(): print(11) gevent.sleep(2) print(33) def f2(): print(22) gevent.sleep(1) prin ...

  4. css处理文字不换行、换行截断、溢出省略号

    1.使文字不换行 white-space: nowrap; 值 描述 normal 默认.空白会被浏览器忽略. pre 空白会被浏览器保留.其行为方式类似 HTML 中的 <pre> 标签 ...

  5. 自定义radio、checkbox的样式

    input标签中的radio和checkbox是很表单中常用的类型,大多时候,默认样式并不能满足我们的需求,所以有了此篇. 自定义样式,由此开启: html: <div class=" ...

  6. ThreadX——IPC应用之信号量

    一.应用简介 在RTOS的应用开发中,信号量也是经常使用到的一种用于多任务之间信息同步.资源互斥访问的一种手段,常用于协调多个任务访问同一资源的场景.信号量又分为计数信号量和互斥信号量.计数信号量可以 ...

  7. 用 shell 脚本做日志清洗

    问题的提出 公司有一个用户行为分析系统,可以记录用户在使用公司产品过程中的一系列操作轨迹,便于分析产品使用情况以便优化产品 UI 界面布局.这套系统有点类似于 Google Analyse(GA),所 ...

  8. Java各版本新增特性, Since Java 8

    Java各版本新增特性, Since Java 8 作者:Grey 原文地址: Github 语雀 博客园 Java 8 Reactor of Java 这一章来自于<Spring in Act ...

  9. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  10. 一段小代码秒懂C++右值引用和RVO(返回值优化)的误区

    关于C++右值引用的参考文档里面有明确提到,右值引用可以延长临时变量的周期.如: std::string&& r3 = s1 + s1; // okay: rvalue referen ...