文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

确保Spring Boot定时任务只执行一次方案

2024-11-29 17:52

关注

1. 简介

在本篇文章中,我们将学习如何控制定时任务仅运行一次。定时任务是自动化诸如报告生成或发送通知等过程的常见做法。通常,我们设置这些任务定期运行。然而,在某些情况下,我们可能希望一个任务在未来的某个时间点仅执行一次,例如初始化资源或进行数据迁移等操作。

接下来将探讨在Spring Boot应用中如何控制定时任务仅运行一次的几种方法。本文将通过带有初始延迟的@Scheduled注解,到更灵活的方法,如TaskScheduler和自定义触发器来避免任务的重复执行。

2. 实战案例

2.1 仅指定开始时间

虽然@Scheduled注解提供了一种直接的任务调度方式,但在灵活性方面做的不够好。当需要对任务规划进行更多控制(比如针对一次性执行的任务)时,Spring的TaskScheduler接口提供了多功能的替代方案。使用TaskScheduler,我们可以以编程方式安排任务,并指定任务的开始时间,从而为动态调度场景提供了更大的灵活性。

TaskScheduler中最简单的方法允许我们定义一个Runnable任务和一个Instant,表示我们希望任务执行的具体时间。这种方法使我们能够动态地安排任务,而不必依赖于固定的注解,如下示例:

public class TaskComponent {
  private TaskScheduler taskScheduler = new SimpleAsyncTaskScheduler() ;
  public void schedule(Runnable task, Instant startTime) {
    taskScheduler.schedule(task, startTime) ;
  }
}

TaskScheduler中的所有其他方法都是用于周期性执行的,因此这个方法对于一次性任务很有帮助。

注:这里我们使用了SimpleAsyncTaskScheduler,如果你的环境是Spring6.1以上版本,那么你还可以通过设置为虚拟线程执行任务。

SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler();
scheduler.setVirtualThreads(true) ;

接下来,我们可以通过如下方式测试该种方式的定时任务执行

@Test
public void testOnceTask() {
  CountDownLatch cdl = new CountDownLatch(1) ;


  scheduler.schedule(() -> {
    cdl.countDown();
    // TODO, 任务Action
  }, Instant.now().plus(Duration.ofSeconds(1))) ;
  latch.await() ;
}

这里仅仅是因为通过单元测试时需要通过CountDownLatch进行协助测试。生产环境无需这样。

2.2 通过@Scheduled方式

我们还可以使用@Scheduled注解并设置初始延迟时间,同时不要设置fixedDelay或fixedRate属性,如下示例:

@Component
public class TaskComponent {
  @Scheduled(initialDelay = 3000)
  public void task() {
    // TODO, 任务Action
  }
}

该方法在指定的初始化3s后执行任务之后不会重复执行。

2.3 自定义触发器

我们还可以通过实现PeriodicTrigger,使用PeriodicTrigger我们可以进行更多的控制任务执行。通过重写nextExecution()方法,以便仅在我们尚未触发的情况下返回下一个执行时间,如下示例:

public class PackTrigger extends PeriodicTrigger {
  public PackTrigger(Instant when) {
    super(Duration.ofSeconds(1)) ;
    Duration difference = Duration.between(Instant.now(), when) ;
    setInitialDelay(difference) ;
  }
  
  @Override
  public Instant nextExecution(TriggerContext triggerContext) {
    if (triggerContext.lastCompletion() == null) {
      return super.nextExecution(triggerContext) ;
    }
    // 返回null后将不会再执行
    return null;
  }
}

由于我们只希望它执行一次,因此可以将周期设置为任意值。最终,需要为我们的初始延迟传递一个Duration,因此我们需要计算任务期望执行时刻与当前时间之间的差值。

重写nextExecution方法,如果完成状态为null,则表示它尚未触发,因此允许它调用默认实现。否则,返回null,这将使此触发器仅执行一次,调用示例如下:

TaskScheduler taskScheduler = new SimpleAsyncTaskScheduler() ;
taskScheduler.schedule(() -> {
  System.out.printf("%s, 执行任务%n", Thread.currentThread().getName()) ;
}, new PackTrigger(Instant.now().plusSeconds(2))) ;

通过此种自定义触发器的方式更加的灵活控制定时任务的执行,也是当前最优的选择。

来源:Spring全家桶实战案例源码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯