SpringBoot实现动态多线程并发定时任务

 更新时间:2022年05月16日 10:16:08   作者:typ1805  
这篇文章主要为大家详细介绍了SpringBoot实现动态多线程并发定时任务,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了SpringBoot实现动态多线程并发定时任务的具体代码,供大家参考,具体内容如下

实现定时任务有多种方式,使用spring自带的,继承SchedulingConfigurer的方式。

一、实现

1、启动类

在启动类添加注解@EnableScheduling开启,不然不起用做。

2、新建任务类

添加注解@Component注册到spring的容器中。

package com.example.demo.task;

import com.example.demo.entity.MyTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;

/**
 * @path:com.example.demo.task.ScheduledTask.java
 * @className:ScheduledTask.java
 * @description:定时任务
 * @author:tanyp
 * @dateTime:2020/7/23 21:37 
 * @editNote:
 */
@Slf4j
@Component
public class ScheduledTask implements SchedulingConfigurer {

    private volatile ScheduledTaskRegistrar registrar;

    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<String, CronTask> cronTasks = new ConcurrentHashMap<>();

    /**
     * 默认启动10个线程
     */
    private static final Integer DEFAULT_THREAD_POOL = 10;

    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        registrar.setScheduler(Executors.newScheduledThreadPool(DEFAULT_THREAD_POOL));
        this.registrar = registrar;
    }

    @PreDestroy
    public void destroy() {
        this.registrar.destroy();
    }

    /**
     * @methodName:refreshTask
     * @description:初始化任务
     * 1、从数据库获取执行任务的集合【TxTask】
     * 2、通过调用 【refresh】 方法刷新任务列表
     * 3、每次数据库中的任务发生变化后重新执行【1、2】
     * @author:tanyp
     * @dateTime:2020/7/23 21:37
     * @Params: [tasks]
     * @Return: void
     * @editNote:
     */
    public void refreshTask(List<MyTask> tasks) {
        // 删除已经取消任务
        scheduledFutures.keySet().forEach(key -> {
            if (Objects.isNull(tasks) || tasks.size() == 0) {
                scheduledFutures.get(key).cancel(false);
                scheduledFutures.remove(key);
                cronTasks.remove(key);
                return;
            }
            tasks.forEach(task -> {
                if (!Objects.equals(key, task.getTaskId())) {
                    scheduledFutures.get(key).cancel(false);
                    scheduledFutures.remove(key);
                    cronTasks.remove(key);
                    return;
                }
            });
        });

        // 添加新任务、更改执行规则任务
        tasks.forEach(txTask -> {
            String expression = txTask.getExpression();
            // 任务表达式为空则跳过
            if (StringUtils.isEmpty(expression)) {
                return;
            }

            // 任务已存在并且表达式未发生变化则跳过
            if (scheduledFutures.containsKey(txTask.getTaskId()) && cronTasks.get(txTask.getTaskId()).getExpression().equals(expression)) {
                return;
            }

            // 任务执行时间发生了变化,则删除该任务
            if (scheduledFutures.containsKey(txTask.getTaskId())) {
                scheduledFutures.get(txTask.getTaskId()).cancel(false);
                scheduledFutures.remove(txTask.getTaskId());
                cronTasks.remove(txTask.getTaskId());
            }
            CronTask task = new CronTask(new Runnable() {
                @Override
                public void run() {
                    // 执行业务逻辑
                    try {
                        log.info("执行单个任务,任务ID【{}】执行规则【{}】", txTask.getTaskId(), txTask.getExpression());
                        System.out.println("==========================执行任务=============================");
                    } catch (Exception e) {
                        log.error("执行发送消息任务异常,异常信息:{}", e);
                    }
                }
            }, expression);
            ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
            cronTasks.put(txTask.getTaskId(), task);
            scheduledFutures.put(txTask.getTaskId(), future);
        });
    }

}

3、创建自启动任务类

package com.example.demo.task;

import com.example.demo.task.ScheduledTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @path:com.example.demo.task.MyApplicationRunner.java
 * @className:ScheduledTask.java
 * @description:自启动
 * @author:tanyp
 * @dateTime:2020/7/23 21:37 
 * @editNote:
 */
@Slf4j
@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Autowired
    private ScheduledTask scheduledTask;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("================项目启动初始化定时任务====开始===========");
        /**
         * 初始化三个任务:
         * 1、10秒执行一次
         * 2、15秒执行一次
         * 3、20秒执行一次
         */
        List<MyTask> tasks = Arrays.asList(
                MyTask.builder().taskId("10001").expression("*/10 * * * * ?").build(),
                MyTask.builder().taskId("10002").expression("*/15 * * * * ?").build(),
                MyTask.builder().taskId("10003").expression("*/20 * * * * ?").build()
        );
        scheduledTask.refreshTask(tasks);
        log.info("================项目启动初始化定时任务====完成==========");
    }
}

4、实体

package com.example.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @path:com.example.demo.entity.MyTask.java
 * @className:MyTask.java
 * @description:任务实体
 * @author:tanyp
 * @dateTime:2020/7/23 21:41 
 * @editNote:
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MyTask {

    /**
     * 任务id
     */
    private String taskId;

    /**
     * 任务执行规则时间
     */
    private String expression;
}

二、测试

初始化三个任务,分别为:

10秒执行一次(*/10 * * * * ?)
15秒执行一次(*/15 * * * * ?)
20秒执行一次(*/20 * * * * ?)

测试效果:

可以看到初始化的三个任务都在执行,并且是不用的线程在执行。

三、动态使用方式

1、启动方式有两种:

  • 启动项目后,手动调用ScheduledTask.refreshTask(List tasks),并初始化任务列表;
  • 使用我测试中的方式,配置项目启动完成后自动调用初始任务的方法,并初始化任务列表。

2、数据初始化

只需要给 List集合赋值并调用refreshTask()方法即可:

  • 根据业务需求修改MyTask实体类;
  • 这里的初始化数据可以从数据库读取数据赋值给集合;

例如:从mysql读取任务配置表的数据,调用refreshTask()方法。

3、如何动态?

  • 修改:修改某一项正在执行的任务规则;
  • 添加:添加一项新的任务;
  • 删除:停止某一项正在执行的任务。

例如:我们有一张任务配置表,此时进行分别新增一条或多条数据、删除一条或多条数据、改一条数据,只需要完成以上任何一项操作后,重新调用一下refreshTask()方法即可。

怎么重新调用 refreshTask()方法:可以另外启一个任务实时监控任务表的数据变化。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

相关文章

  • Mybatis日志模块的适配器模式详解

    Mybatis日志模块的适配器模式详解

    这篇文章主要介绍了Mybatis日志模块的适配器模式详解,,mybatis用了适配器模式来兼容这些框架,适配器模式就是通过组合的方式,将需要适配的类转为使用者能够使用的接口
    2022-08-08
  • SpringCloud开发课程查询功能

    SpringCloud开发课程查询功能

    这篇文章主要介绍了SpringCloud开发课程查询功能,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • java中实现汉字按照拼音排序(示例代码)

    java中实现汉字按照拼音排序(示例代码)

    这篇文章主要是对java中将汉字按照拼音排序的实现代码进行了详细的分析介绍。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12
  • JAVA实现扫描线算法(超详细)

    JAVA实现扫描线算法(超详细)

    扫描线算法就是从Ymin开始扫描,然后构建出NET,之后根据NET建立AET。接下来本文通过代码给大家介绍JAVA实现扫描线算法,感兴趣的朋友一起看看吧
    2019-10-10
  • Idea如何配置Maven才能优先从本地仓库获取依赖(亲测方法有效)

    Idea如何配置Maven才能优先从本地仓库获取依赖(亲测方法有效)

    对于Idea怎么配置Maven才能优先从本地仓库获取依赖,网上说法有很多种,都不太靠谱,最终都没有效果,最好的解决方法是通过修改maven配置文件settings.xml,本文给大家介绍的非常详细,需要的朋友参考下吧
    2023-10-10
  • java反射方式创建代码详解

    java反射方式创建代码详解

    在本篇文章里小编给大家整理的是一篇关于java反射方式创建代码详解内容,对此有兴趣的朋友们可以学习下。
    2021-01-01
  • SpringBoot配置 Druid 三种方式(包括纯配置文件配置)

    SpringBoot配置 Druid 三种方式(包括纯配置文件配置)

    本文给大家分享在项目中用纯 YML(application.yml 或者 application.properties)文件、Java 代码配置 Bean 和注解三种方式配置 Alibaba Druid 用于监控或者查看 SQL 状况的相关知识,感兴趣的朋友一起看看吧
    2021-10-10
  • Java程序员需要掌握的英语词组

    Java程序员需要掌握的英语词组

    这篇文章主要为大家详细汇总了Java程序员需要掌握的英语词组 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • Java三大特性之多态详解

    Java三大特性之多态详解

    多态是继封装、继承之后,面向对象的第三大特性。多态: 是指同一行为,具有多个不同表现形式。本文将来和大家详细说说Java中的多态,需要的可以了解一下
    2022-10-10
  • 详解Java中的线程模型与线程调度

    详解Java中的线程模型与线程调度

    这篇文章主要介绍了详解Java中的线程模型与线程调度的相关资料,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-02-02

最新评论