ThreadPoolTaskScheduler实现定时任务的动态管理(启停、增加、删除、周期修改)

之前接触过基于SchedulingConfigurer接口的定时任务,发现其在动态管理方面并不是很方便本文的内容是利用ThreadPoolTaskScheduler手写调度中心,实现定时任务的动态管理,但它也有一个缺点就是不支持分布式任务调度。

其实Java中实现定时任务的方式很多,现在的Quartz和xxl-job框架在定时任务方面其实已经做的非常优秀了,甚至支持分布式。但学习得有一个过程,所以打算通过一个简单的实践来学习一下ThreadPoolTaskScheduler的使用。

 一、数据库表准备

在数据库中创建job_tb表,它的每条数据代表一个定时任务实例。

DROP TABLE IF EXISTS `job_tb`;
CREATE TABLE `job_tb` (
  `id` varchar(255) NOT NULL,
  `jobName` varchar(255) NOT NULL,
  `className` varchar(255) NOT NULL,
  `method` varchar(255) NOT NULL,
  `cron` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of job_tb
-- ----------------------------
INSERT INTO `job_tb` VALUES ('w0o9pzCQAMJ5PQN5', '开始学习', 'com.hedong.configuration.ScheduledMethodsConfig', 'method1', '0/10 * * * * ?');

 代码中对应的Job模型

package com.hedong.model;

import lombok.Data;

/**
 * @author: DoNg
 * @create: 2020-12-24 20:59
 **/
@Data
public class Job {

    //任务id
    private String id;

    //任务名称
    private String jobName;

    //类名
    private String className;

    //方法名
    private String method;

    //cron表达式
    private String cron;

}

 二、任务调度核心类

这里直接给出了任务调度的核心类,所有的核心操作都在这个类中,包括添加任务、删除任务、修改任务、查询任务、启动及暂停任务。

package com.hedong.service.impl;

import com.hedong.mapper.JobMapper;
import com.hedong.model.Job;
import com.hedong.service.JobService;
import com.hedong.utils.threads.MyJobRunnable;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * @author: DoNg
 * @create: 2020-12-24 21:10
 **/
@Service
public class JobServiceImpl implements JobService {
    @Autowired
    private JobMapper jobMapper;
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    //存放任务调度的容器,此容器中的任务都是正在运行的任务
    private ConcurrentHashMap<String, ScheduledFuture> futureMap = new ConcurrentHashMap<>();

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(10);
        return threadPoolTaskScheduler;
    }

    /**
     * @Description: 增加任务
     * @Author: DoNg
     */
    @Override
    public String add(Job job) {
        //设置随机字符串作为id
        job.setId(RandomStringUtils.randomAlphanumeric(16));
        int result = jobMapper.add(job);
        if (result > 0) {
            return "增加任务成功!";
        } else {
            return "增加任务失败!";
        }
    }

    /**
     * @Description: 删除任务
     * @Author: DoNg
     */
    @Override
    public String delete(String id) {
        //先尝试暂停任务
        pause(id);
        //从数据库中删除
        int result = jobMapper.delete(id);
        if (result > 0) {
            return "删除成功!";
        } else {
            return "删除失败!";
        }
    }

    /**
     * @Description: 修改任务配置
     * @Author: DoNg
     */
    @Override
    public String edit(Job job) {
        int result = jobMapper.edit(job);
        if (result > 0) {
            //如果该任务正在调度中运行,则重启
            ScheduledFuture future = futureMap.get(job.getId());
            if (future != null) {
                pause(job.getId());
                start(job.getId());
            }
        }
        return "修改任务配置成功!";
    }

    /**
     * @Description: 查询任务列表
     * @Author: DoNg
     */
    @Override
    public List<Job> list(String status) {
        if ("0".equals(status)) {
            //查询数据库中全部任务列表
            return jobMapper.list();
        } else {
            //查询调度集合中正在运行的任务列表
            List<Job> jobRunnings = new ArrayList<>();
            futureMap.forEach((k, v) -> {
                jobRunnings.add(jobMapper.findJobById(k));
            });
            return jobRunnings;
        }
    }

    /**
     * @Description: 启动任务
     * @Author: DoNg
     */
    @Override
    public String start(String id) {
        //获取job实体
        Job job = jobMapper.findJobById(id);
        if (job == null) {
            return "任务不存在,无法启动!";
        }
        if (futureMap.get(job.getId()) != null) {
            return "任务已经在运行,无法重复启动!";
        }

        //回调
        ScheduledFuture future = threadPoolTaskScheduler.schedule(new MyJobRunnable(job), new CronTrigger(job.getCron()));
        //放入任务调度集合中
        futureMap.put(job.getId(), future);
        return "任务启动成功!";
    }


    /**
     * @Description: 暂停任务
     * @Author: DoNg
     */
    @Override
    public String pause(String id) {
        //检测该任务是否在任务调度集合中
        ScheduledFuture future = futureMap.get(id);
        if (future == null) {
            return "该任务不在运行行列中,无法暂停!";
        } else {
            //从调度集合中移除
            future.cancel(true);
            futureMap.remove(id);
            return "任务暂停成功!";
        }
    }

}

 回调中的第一个参数需要一个Runnable类型的线程类,这里我们写一个类去实现Runnable,在run方法中通过job的属性得到具体要执行的方法并调用,即可实现任务的执行。

package com.hedong.utils.threads;

import com.hedong.model.Job;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;

/**
 * @author: DoNg
 * @create: 2020-12-24 21:27
 **/
public class MyJobRunnable implements Runnable{
    private Job job;

    public MyJobRunnable(Job job){
        this.job = job;
    }

    @Override
    public void run() {
        //计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Class<?> clazz;
        try {
            //加载类
            clazz = Class.forName(job.getClassName());
            //获取方法
            Method method = clazz.getMethod(job.getMethod(), Job.class);
            //调用方法
            method.invoke(clazz.newInstance(), job);
        } catch (Exception e) {
            e.printStackTrace();
        }
        stopWatch.stop();
        System.out.println("任务:【" + job.getJobName() + "】执行结束,耗时:" + stopWatch.getTotalTimeMillis());
    }

}

 最后可以创建一个类用于存储各种定时任务执行的方法,以后每次要写一个新的定时任务时,我们只需在该类中添加一个新的方法,把任务要执行的逻辑写在里面,添加定时任务的时候配置好类名、方法、cron等参数就可以了。

package com.hedong.configuration;

import com.hedong.model.Job;

/**
* @Description: 存放每个定时任务需要执行的方法体
* @Param:
* @return:
* @Author: DoNg
*/

public class ScheduledMethodsConfig {
    /**
    * @Description: 任务一
    * @Param: 
    * @return: 
    * @Author: DoNg
    */ 
    public void method1(Job job) throws InterruptedException {
        System.out.println("任务:【" + job.getJobName() + "】正在执行中。。。。。");
        //模拟任务耗时
        Thread.sleep(5000);
    }

    /**
     * @Description: 任务二
     * @Param:
     * @return:
     * @Author: DoNg
     */
    public void method2(Job job) throws InterruptedException {
        System.out.println("任务:【" + job.getJobName() + "】正在执行中。。。。。");
        //模拟任务耗时
        Thread.sleep(3000);
    }
}

最终一个简单的任务调度中心就手写完成了,本文只给出了核心的代码段。项目启动后,我们可以手动开启任意的定时任务,根据需要进行任务的配置修改而不需要重启项目,并且可以随时暂停任务及增添任务内容,这些操作都可以通过前端界面操作完成。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大何向东流1997

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值