springboot定时任务-根据DB自动追加以及任务动态关闭

需求:用户可以设定在未来某天发送一个邮件(发邮件还是干什么视读者自身情况而定)。

难点:

  1. 让用户们追加的任务能够及时地刷新在待执行列表中。(排序、另开任务固定频率读取数据库的任务信息)
  2. 时间点A被注册的任务如何在时间点B(也许是用户主动取消,也许是固定逻辑需要)还能被关闭。(存放已被注册的ScheduledFuture对象以进行控制)。
  3. 如果下一个将要执行今晚八点的任务,而突然有个用户追加了一个七点半要执行的任务该如何进行插队,总不能因为马上要执行八点的就把现在到七点五十九的任务无视。

环境:IDEA+SpringBoot(springboot自带定时任务)+MyBatis(MySql)


一、启用定时任务

1 编写一个类来存放我们的定时任务相关代码。
并在其上增加开启计划任务注解以及开启异步注解

@Component
@EnableScheduling
@EnableAsync

2 在其中注入一个我们需要的线程池,以防springboot采用自己默认的单线程处理。原因在于@EnableScheduling->SchedulingConfiguration->ScheduledAnnotationBeanPostProcessor->ScheduledTaskRegistrar->scheduleTasks()方法有如下判断:

protected void scheduleTasks() {
		if (this.taskScheduler == null) {
			this.localExecutor = Executors.newSingleThreadScheduledExecutor();
			this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
		}
		......
}

所以我们在自己的测试类中用@Bean注册一个线程池:

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

3 接下来我们写一个void方法,在其上增加@Scheduled注解来表明这是一个计划,并追加@Async来开启异步。

@Async
@Scheduled(cron = "0/5 * * * * *")

二、逻辑理解
       此刻我想要每隔五秒读取所有任务信息,并将读取到的任务注册,执行后的任务需要关闭掉,所以需要以下代码(最后会粘出完整代码)

1 每隔一段时间查询一次数据,为了测试方便设定五秒。那么就需要写一个定时任务,每隔五秒查询数据,并且这个任务需要保持。

	@Async
    @Scheduled(cron = "0/5 * * * * *")
    public void first() throws InterruptedException {
        log.info("调度前list的size是:"+this.tasks.size());
        //调用数据库进行查询
        this.tasks = taskService.getMailTasks();
        log.info("调度后list的size是:"+this.tasks.size());
    }

运行如下:
在这里插入图片描述在这里插入图片描述
可以看到相差五秒,这样就能以固定间隔获取任务信息了。

2 任务信息里面有根据用户设置时间而解析出的cron表达式,那么查询后有了对象集合就需要将他们封装成任务。

a.想要把读取到的信息转化为计划任务,就需要依赖线程池的threadPoolTaskScheduler.schedule(runnable,cronTrigger)方法,我们把其他任务信息在runnable中用来控制处理逻辑,把解析的cron表达式传进第二个参数对象中设定时间。

b.由于a提到的方法会返回一个ScheduledFuture对象,并且关闭任务也需要这个对象来执行cancle()方法,所以我们用一个ConcurrentHashMap来存放,以任务的ID为键,以这个ScheduledFuture对象为值。

完整代码如下,被注调的mailService是我发邮件的处理,各位自行改变:

c.当一个任务结束时,我们遍历这个map,找到对应的任务,调用值里ScheduledFuture对象的cancle(true)方法关闭任务,并从这个Map中remove调对象

for(MailModel mail:tasks){
            log.info("现在是任务:"+mail.getTid());
            futures.put(mail.getTid().toString(),threadPoolTaskScheduler.schedule(new Runnable() {

                @Override
                public void run() {
//                    mailService.sendSimpleMail(mail.getToUser(),mail.getMailTitle(),mail.getMailContents());
                    System.out.println("任务执行!变量设置为1");
                    System.out.println(mail.getMailContents());
                    System.out.println(mail.toString());
                    System.out.println("遍历map");
                    for(Map.Entry<String,ScheduledFuture<?>> scheduledFutureEntry:futures.entrySet()){
                        if(scheduledFutureEntry.getKey().equals(mail.getTid().toString())&&mail.getTid()==1){
                            System.out.println("找到了匹配的这个任务,执行关闭,并在map中清除");
                            scheduledFutureEntry.getValue().cancel(true);
                            futures.remove(scheduledFutureEntry.getKey());
                            System.out.println("清除后任务容量:"+futures.size());
                        }
                    }
                }
            }, new CronTrigger(mail.getTchron())));

运行结果:可以看到我的两个任务的定时,一个是每隔五秒打印,一个是每隔10秒打印,现在根据我逻辑中这段代码:
在这里插入图片描述

if(scheduledFutureEntry.getKey().equals(mail.getTid().toString())&&mail.getTid()==1)

这个五秒一执行且id为1的任务将在第一次执行后就被关闭且移除,而十秒一执行的任务将会一直存在。我们看看运行结果:

首先,定时查询任务启动,将数据库两个任务信息读出并执行,显示map中两个任务:
在这里插入图片描述
然后,五秒一执行的任务先触发,并在执行后将自己结束并移除,容量剩一个。
在这里插入图片描述

最后,十秒一执行的任务由于不满足id==1,所以一直按照预定的计划运作了起来。
在这里插入图片描述


小结:到这里大致思路就结束了,我们可以从库里动态获取任务,也可以根据任务的唯一标识存/取他的future对象,那么剩下的何时关,何时开,何时读,何时写,何时操作map,相关的对象都已经在手,剩下的根据自己的功能去实现即可。

如有不对请务必为我指正,如有帮助记得点个赞呀。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值