动态定时任务ThreadPoolTaskScheduler

在工作中出现了这样一个需求,用户可以制定多个任务,每个任务可以定时分配给多个人。任务的属性包括:任务巡检周期、任务工作日历、巡检人员。
这就需要我们开发一个动态的定时任务功能,而且这个定时任务是可以随时新增、更改、停止的。刚开始接到这个需求的时候我就直接说,我一两天开发不完QwQ,但其实这个功能一点也不难,只是我不知道还有ThreadPoolTaskScheduler这种简便的开发工具罢了,自己菜怨不得需求难。
但是一开始还是走了一些弯路的:
package cn.dxsc.safety.web.config;

import cn.dxsc.safety.common.util.ChinaHolidaysUtils;
import cn.dxsc.safety.common.util.StreamUtils;
import cn.dxsc.safety.common.util.StringUtils;
import cn.dxsc.safety.dao.mapper.TaskDistributionMapper;
import cn.dxsc.safety.dao.mapper.TroubleCheckTaskDistributionMapper;
import cn.dxsc.safety.entity.po.TaskDistribution;
import cn.dxsc.safety.entity.po.TroubleCheckTaskDistribution;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class AutoDistributionTask implements SchedulingConfigurer {

//这里有一些是我的业务代码,没有删

    @Autowired
    private TroubleCheckTaskDistributionMapper troubleCheckTaskDistributionMapper;

    @Autowired
    private TaskDistributionMapper taskDistributionMapper;

    private List<TroubleCheckTaskDistribution> troubleCheckTaskDistributions;

    /**
     * 初始化时调用
     * 目的:为动态定时任务赋予初始值
     */
    @PostConstruct
    public void doConstruct() {
        this.troubleCheckTaskDistributions = troubleCheckTaskDistributionMapper.selectList(null);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        for (TroubleCheckTaskDistribution troubleCheckTaskDistribution : troubleCheckTaskDistributions) {
            //第一个动态定时任务
            // 动态使用cron表达式设置循环间隔
            taskRegistrar.addTriggerTask(() -> System.out.println("Current time: " + DateUtil.now()), triggerContext -> {
                // 使用CronTrigger触发器,可动态修改cron表达式来操作循环规则
                CronTrigger cronTrigger = new CronTrigger(troubleCheckTaskDistribution.getCron());
//                //--------此处替换执行操作
//                String workType = troubleCheckTaskDistribution.getWorkType();
//                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//                boolean publicHolidays = false;
//                try {
//                    publicHolidays = ChinaHolidaysUtils.isPublicHolidays(simpleDateFormat.format(new Date()));
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//                //判断工作日类型
//                if(new Date().after(troubleCheckTaskDistribution.getBeginTime()) && !StringUtils.isEmpty(workType))
//                    if ((workType.equals("法定工作日") && publicHolidays)||(workType.equals("非法定工作日") && !publicHolidays)||workType.equals("每天")){
//                        List<TaskDistribution> taskDistributions = taskDistributionMapper.selectList(new LambdaQueryWrapper<TaskDistribution>().eq(TaskDistribution::getTroubleCheckTaskDistributionId, troubleCheckTaskDistribution.getId()))
//                                .stream().filter(StreamUtils.distinctByKey(TaskDistribution::getCheckPeople)).collect(Collectors.toList());
//                        if(taskDistributions.size() != 0){
//                            TaskDistribution taskDistribution = new TaskDistribution();
//                            taskDistribution.setBeginTime(new Date());
//                            taskDistribution.setTroubleCheckTaskDistributionId(troubleCheckTaskDistribution.getId());
//                            taskDistribution.setCheckCompany(taskDistributions.get(0).getCheckCompany());
//                            taskDistributionMapper.insert(taskDistribution);
//                        }
//                    }
                return cronTrigger.nextExecutionTime(triggerContext);
            });
        }
    }
}
一开始百度到了这样一个方法,可以为动态定时任务赋予初始值,在doConstruct()里获取到了当前数据库里所有的任务的,然后加入到taskRegistrar里面去执行,获取每个任务生成的cron表达式,然后处理业务巴拉巴拉。然后系统一部署后我就不能在新增、修改、删除定时任务了,这怎么能行。
马上,第二种方法:

1、首先创建配置类

package cn.dxsc.safety.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

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

/**
 * @author mrChen
 * @date 2024/2/23 9:16
 */
@Configuration
public class ThreadPoolConfig {
    public static ConcurrentHashMap<String, ScheduledFuture> cache = new ConcurrentHashMap<String, ScheduledFuture>();

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(100);
        threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
        threadPoolTaskScheduler.setAwaitTerminationSeconds(10);
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        return threadPoolTaskScheduler;
    }
}

2、然后是一个任务添加、删除的统一处理

package cn.dxsc.safety.web.config;

import org.springframework.scheduling.SchedulingException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * @author mrChen
 * @date 2024/2/22 21:39
 */
@Component
public class CronTask {

    final
    ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<String, ScheduledFuture<?>>();

    public CronTask(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
    }

    /**
     * 添加任务
     *
     * @param taskId
     * @param triggerTask
     */
    public void addTriggerTask(String taskId, TriggerTask triggerTask) {
        if (taskFutures.containsKey(taskId)) {
            throw new SchedulingException("the taskId[" + taskId + "] was added.");
        }
        ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
        taskFutures.put(taskId, schedule);

    }

    /**
     * 取消任务
     *
     * @param taskId
     */
    public void cancelTriggerTask(String taskId) {
        ScheduledFuture<?> future = taskFutures.get(taskId);
        if (future != null) {
            future.cancel(true);
        }
        taskFutures.remove(taskId);
    }
}

3、最后是我的业务代码

@PutMapping("/update")
    @ApiOperation("根据主键修改")
    public R update(@RequestBody TroubleCheckTaskDistributionDto dto) {
        TroubleCheckTaskDistribution h=new TroubleCheckTaskDistribution();
        BeanUtils.copyProperties(dto, h);
        String inspCycle = dto.getInspCycle();
        String inspUnit = dto.getInspUnit();
        Date beginTime = dto.getBeginTime();
        int year = beginTime.getYear();
        int month = beginTime.getMonth() + 1;
        int day = beginTime.getDay() + 4;
        int hours = beginTime.getHours();
        int minutes = beginTime.getMinutes();
        int seconds = beginTime.getSeconds();

        //生成cron
        String cron = "";
        switch (inspUnit) {
            case "小时":
                cron = seconds+" "+minutes+" 0/" + inspCycle + " * * ?";
                break;
            case "天":
                cron = seconds+" "+minutes+" "+hours+" */"+inspCycle+" * ?";//在这个表达式中,“001"表示任务每天的凌晨1点开始执行,"*/3"表示任务每隔三天执行一次,“*"表示任何月份都可以执行,“?”表示不考虑星期几的条件。
                break;
            case "月":
                cron = seconds+" "+minutes+" "+hours+" "+day +" "+"1/"+inspCycle+" ?";
                break;
            case "年":
                cron = seconds+" "+minutes+" "+hours+" "+day+" "+month+" ?";
                break;
        }
        h.setCron(cron);
        boolean b = iTroubleCheckTaskDistributionService.updateById(h);

        if (taskHashMap.size() >= 50){
            return R.fail("所设置的任务已经超标!");
        }

        if(this.taskHashMap.containsKey(h.getId())) {
            cronTask.cancelTriggerTask(String.valueOf(h.getId()));
            taskHashMap.remove(h.getId());
        }
        this.taskHashMap.put(h.getId(), cron);
        TriggerTask triggerTask = new TriggerTask(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Current time: " + DateUtil.now() + "执行任务:" + h.getId());
                        //--------此处替换执行操作
                        String workType = h.getWorkType();
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        boolean publicHolidays = false;
                        try {
                            publicHolidays = ChinaHolidaysUtils.isPublicHolidays(simpleDateFormat.format(new Date()));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        //判断工作日类型
                        if (new Date().after(h.getBeginTime()) && !StringUtils.isEmpty(workType))
                            if ((workType.equals("法定工作日") && publicHolidays) || (workType.equals("非法定工作日") && !publicHolidays) || workType.equals("每天")) {
//                                List<TaskDistribution> taskDistributions = taskDistributionMapper.selectList(new LambdaQueryWrapper<TaskDistribution>().eq(TaskDistribution::getTroubleCheckTaskDistributionId, h.getId()))
//                                        .stream().filter(StreamUtils.distinctByKey(TaskDistribution::getCheckPeople)).collect(Collectors.toList());
                                List<TaskDistribution> taskDistributions = dto.getTaskDistributions();
                                if (taskDistributions.size() != 0) {
                                    for (TaskDistribution taskDistribution : taskDistributions) {
                                        taskDistribution.setBeginTime(new Date());
                                        taskDistribution.setId(null);
                                        taskDistributionMapper.insert(taskDistribution);
                                    }
                                }
                            }
                    }
                },
                new CronTrigger(this.taskHashMap.get(h.getId()))
        );
        cronTask.addTriggerTask(String.valueOf(h.getId()), triggerTask);
        return b ? R.ok() : R.fail();
    }

一个集坚强与信心于一身的菇凉。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ThreadPoolTaskScheduler 是 Spring 框架中提供的一个线程池定时任务调度器,可以在预定的时间间隔或固定的时间执行任务。它可以用于执行异步任务,如发送邮件或者短信,定时从数据库或者第三方接口获取数据等。 ThreadPoolTaskScheduler 可以通过配置线程池的大小来控制并发执行任务的数量,从而避免系统资源浪费。它也提供了一些方法来控制定时任务的执行,如取消任务、暂停任务、恢复任务等。 下面是一个使用 ThreadPoolTaskScheduler 调度定时任务的示例: 首先,需要在 Spring 配置文件中配置 ThreadPoolTaskScheduler定时任务: ```xml <bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"> <property name="poolSize" value="5"/> </bean> <bean id="myTask" class="com.example.MyTask"/> <task:scheduled-tasks> <task:scheduled ref="myTask" method="run" fixed-delay="5000"/> </task:scheduled-tasks> ``` 其中,taskScheduler 配置了线程池大小为 5,myTask 是一个实现了 Runnable 接口的类,run 方法是定时任务要执行的方法,fixed-delay 属性表示每隔 5 秒执行一次。 接着,在 MyTask 类中实现 run 方法: ```java public class MyTask implements Runnable { @Override public void run() { // 执行定时任务的逻辑 System.out.println("定时任务执行了!"); } } ``` 这样就可以使用 ThreadPoolTaskScheduler 定时执行 MyTask 中的 run 方法了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

淡雅的惆怅

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

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

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

打赏作者

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

抵扣说明:

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

余额充值