玩转SpringBoot之定时任务@Scheduled线程池配置

序言

对于定时任务,在SpringBoot中只需要使用@Scheduled 这个注解就能够满足需求,它的出现也给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了。

但是,我们需要注意的是, @Scheduled 并不一定会按时执行

因为使用@Scheduled 的定时任务虽然是 异步执行的,但是,不同的定时任务之间 并不是并行的!!!!!!!!

在其中一个定时任务没有执行完之前,其他的定时任务即使是到了执行时间,也是不会执行的,它们会进行排队。

也就是如果你想你不同的定时任务互不影响,到时间就会执行,那么你最好将你的定时任务方法自己搞成异步方法,这样,定时任务其实就相当于调用了一个线程执行任务,一瞬间就结束了。比如使用: @Async

当然,也可以勉强将你的定时任务当做都会定时执行。但是,作为一个合格的程序员

那么,如何将@Scheduled实现的定时任务变成异步的呢?此时你需要对@Scheduled进行线程池配置。

配置示例

package com.java.navtool.business.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

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

/**
 * @author :mmzsblog.cn
 * @date :Created in 2021/7/27 17:46
 * @description:spring-boot 多线程  @Scheduled注解 并发定时任务的解决方案
 * @modified By:
 * @version:
 */

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    public static final String EXECUTOR_SERVICE = "scheduledExecutor";

    @Bean(EXECUTOR_SERVICE)
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 设置最大线程数
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 10);
        // 设置队列容量
        executor.setQueueCapacity(Runtime.getRuntime().availableProcessors() * 10);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(10);
        // 设置默认线程名称
        executor.setThreadNamePrefix("scheduled-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

}

附带介绍一下线程池的几个参数。需要彻底搞懂,不要死记硬背哦!

线程池参数

  • 1、corePoolSize(必填):核心线程数。
  • 2、maximumPoolSize(必填):最大线程数。
  • 3、keepAliveTime(必填):线程空闲时长。如果超过该时长,非核心线程就会被回收。
  • 4、unit(必填):指定keepAliveTime的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  • 5、workQueue(必填):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该队列中。
  • 6、threadFactory(可选):线程工厂。一般就用默认的。
  • 7、handler(可选):拒绝策略。当线程数达到最大线程数时就要执行饱和策略。

说下核心线程数和最大线程数的区别:

拒绝策略可选值:

  • 1、AbortPolicy(默认):放弃任务并抛出RejectedExecutionException异常。
  • 2、CallerRunsPolicy:由调用线程处理该任务。
  • 3、DiscardPolicy:放弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
  • 4、DiscardOldestPolicy:放弃队列最早的未处理任务,然后重新尝试执行任务。

线程池执行流程:

上个流程图,先试着自己看下能不能看懂:

玩转SpringBoot之定时任务@Scheduled线程池配置

简短的总结下线程池执行流程:

  • 1、一个任务提交到线程池后,如果当前的线程数没达到核心线程数,则新建一个线程并且执行新任务,注意一点,这个新任务执行完后,该线程不会被销毁;
  • 2、如果达到了,则判断任务队列满了没,如果没满,则将任务放入任务队列;
  • 3、如果满了,则判断当前线程数量是否达到最大线程数,如果没达到,则创建新线程来执行任务,注意,如果线程池中线程数量大于核心线程数,每当有线程超过了空闲时间,就会被销毁,直到线程数量不大于核心线程数;
  • 4、如果达到了最大线程数,并且任务队列满了,就会执行饱和策略;

Original: https://www.cnblogs.com/mmzs/p/16057742.html
Author: 淼淼之森
Title: 玩转SpringBoot之定时任务@Scheduled线程池配置

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/542036/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 面试题:Java中为什么只有值传递?

    作者:小牛呼噜噜 | https://xiaoniuhululu.com计算机内功、JAVA底层、面试相关资料等更多精彩文章在公众号「小牛呼噜噜 」 经典的问题 形参&实参…

    Java 2023年6月15日
    093
  • 单元测试、反射、注解、动态代理

    单元测试JUnit 单元测试的目的是针对方法进行测试, JUnit的两个要点:①必须是公开的,无参数,无返回值的方法 ②测试方法必须使用@Test注解标记 public class…

    Java 2023年6月9日
    074
  • 基础回顾-线程的几种状态

    我的博客即将同步至 OSCHINA 社区,这是我的 OSCHINA ID:lonecloud,邀请大家一同入驻:https://www.oschina.net/sharing-pl…

    Java 2023年5月30日
    087
  • 包装类

    包装类 JDK5以前需要我们手动装箱和拆箱装箱就是基本数据类型转换成包装类型拆箱就是包装类型转换成基本数据类型自动装箱底层使用的是Integer.valueOf()方法自动拆箱底层…

    Java 2023年6月5日
    096
  • Spring Cloud 配置application.yml与bootstrap.yml快速入门

    一、概述 _Spring Cloud Config_是 Spring 的客户端/服务器方法,用于跨多个应用程序和环境存储和提供分布式配置。 此配置存储理想地在 Git_版本控制下进…

    Java 2023年5月30日
    092
  • 以Integer类型传参值不变来理解Java值传参

    最近在写代码的时候出了一个错误,由于对值引用理解的不深,将Integer传入方法中修改,以为传入后直接修改Integer中的值就不用写返回值接收了,虽然很快发现了问题,但还是来总结…

    Java 2023年5月29日
    083
  • Java使用HTTPS登录网站代码实现

    步骤一,获得网站的登录URL,及Form页元数据 这点可以通过Chrome的F12按键,检查登录时发送的数据包获得(【Preserve Log】建议打勾)。以登录京东网站为例,示范…

    Java 2023年5月29日
    0167
  • 格式化的输出

    可以使用 System.out.print(s)将数值输出到控制台中; Java SE 5.0沿用了C语言库函数中的printf方法,例如: System.out.printf(“…

    Java 2023年6月9日
    075
  • emqtt使用ssl链接

    emqtt需要配置ssl证书才能启用ssl链接,配置如下。 找到etc目录下的listeners.conf 替换自己的ssl证书路径 测试链接 Original: https://…

    Java 2023年5月29日
    0105
  • java数字格式化DecimalFormat

    保留两位有效数字可以使用DecimalFormat来实现; String str = "195.1"; BigDecimal bigDecimal = new …

    Java 2023年5月29日
    087
  • 关键字、标识符和保留字

    关键字 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)特点:关键字中所字母都为小写 保留字 具体哪些保留字:goto 、const注意: 1、自己命名标识符时要避…

    Java 2023年6月7日
    076
  • java.lang.ClassNotFoundException: com.*.*.entity.time.Q*

    添加依赖 1、添加DSL依赖 2、添加插件 Original: https://www.cnblogs.com/tianciliangen/p/11693647.htmlAutho…

    Java 2023年5月29日
    079
  • Java正则表达式

    不包含thumb.jpg,但包含_HH_,并以.jpg结尾 不包含_Check.xml,但以.xml结尾 Original: https://www.cnblogs.com/gis…

    Java 2023年5月29日
    069
  • 3. SpringBoot整合Redis

    1.下载地址以及安装 https://github.com/MicrosoftArchive/redis/releases 我是安装的windows版本所以选择这个: 解压之后运行…

    Java 2023年6月9日
    0102
  • java.net.NoRouteToHostException: 没有到主机的路由

    今天在配置Jenkins 的云服务器的时候提示:java.net.NoRouteToHostException: 没有到主机的路由,网上查到的没有主机路由问题提到的大多是防火墙问题…

    Java 2023年5月29日
    067
  • 64位虚拟机Guest OS安装错误:0xC0000225

    1, CPU必须支持Intel VT-x/AMD-V。并且在BIOS及虚拟机软件中启用。 2, 虚拟机要启用 IO ACPI。 Original: https://www.cnbl…

    Java 2023年5月30日
    069
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球