计划任务功能在应用程序及其常见,使用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. 快速排序(c++,递归)quick_sort

    放上c++代码,模板 1 #include <iostream> 2 #include<bits/stdc++.h> 3 using namespace std; 4 5 in ...

  2. 【运行机制】 JavaScript的事件循环机制总结 eventLoop

    0.从个例子开始 //code-01 console.log(1) setTimeout(() => { console.log(2); }); console.log(3); 稍微有点前端经验 ...

  3. 斜率优化DP复习笔记

    前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...

  4. Codeforces Edu Round 58 A-E

    A. Minimum Integer 如果\(d < l\),则\(d\)满足条件 否则,输出\(d * (r / d + 1)\)即可. #include <cstdio> #in ...

  5. 安卓实用工具箱v4.3几百种小功能

    款多功能实用工具箱.提供了从日常.图片.查询.设备.辅助.提取.优惠券.趣味游戏等多方面的功能,操作简单,即点即用,避免您下载超多应用的难题,且应用体积轻巧,界面简洁.已去除广告! 下载地址:http ...

  6. JavaSE25-Junit&注解

    1.Junit单元测试 1.1 测试分类 1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值. 2. 白盒测试:需要写代码的.关注程序具体的执行流程. Junit使用:白盒测试 步骤: ...

  7. Linux下MySQL数据库的备份与恢复

    Linux下MySQL数据库的备份与恢复 作者:Grey 原文地址: Github 语雀 博客园 基于版本 MySQL5.7 Deepin Linux 15.11 xtrabackup-2.4.18 ...

  8. 轮廓检测论文解读 | Richer Convolutional Features for Edge Detection | CVPR | 2017

    有什么问题可以加作者微信讨论,cyx645016617 上千人的粉丝群已经成立,氛围超好.为大家提供一个遇到问题有可能得到答案的平台. 0 概述 论文名称:"Richer Convoluti ...

  9. 04-flask-模版基础

    Jinja2 概念 Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Fl ...

  10. 关于Java Integer和Long直接比较

    Integer和Long不能直接equals比较会返回False Long.class源码 ` public boolean equals(Object obj) { if (obj instance ...