写点什么

Java & Go 定时任务

作者:FunTester
  • 2024-07-25
    河北
  • 本文字数:3634 字

    阅读完需:约 12 分钟

定时任务概括

定时任务是软件开发中用于自动执行周期性任务的技术。它允许开发者设置特定的时间点或间隔来触发预定的操作,如数据备份、清理缓存、发送通知等。这种机制可以减少人工干预,提高系统效率和稳定性。定时任务在不同的操作系统和编程环境中有不同的实现方式,例如 Linux 的 cron 作业、Windows 的任务计划程序,或是编程语言中的相关库。它们帮助自动化重复性工作,确保关键任务按时执行,是现代应用程序不可或缺的组成部分。

定时任务使用场景

定时任务在软件开发当中使用非常广泛。主要有以下场景:


  1. 数据备份。定时备份重要数据,防止丢失。

  2. 日志清理。定时归档和清理日志文件,释放磁盘空间

  3. 性能监控。定时收集、处理和上报性能数据。

  4. 数据同步。定时将最新数据同步给其他消费者。

  5. 资源管理。定时清理、回收系统资源,提升利用率和性能。


当然列举的这几个有些宽泛,在实际开发当中,会有多种多样的定时任务场景。下面我们先来看看 Java 语言都有哪些实现定时任务的类库。

Java 语言实现定时任务

相信很多小伙伴接触最多的定时任务就是定时自动化回归测试了。通常会有专门的开发和测试框架来完成具体的设置和执行定时任务。在 Java 语言中,实现定时任务有几种常用的方法:


  1. java.util.Timer:这是 Java 标准库提供的一个类,可以用来安排任务以后在后台线程中执行。使用Timer类,你可以创建一个TimerTask任务,然后使用schedulescheduleAtFixedRate方法来安排任务的执行。

  2. ScheduledExecutorService 接口:这是 Java 并发包中的一部分,提供了更灵活的定时任务调度能力。你可以使用Executors类创建一个ScheduledExecutorService实例,然后使用schedulescheduleAtFixedRate方法来安排任务。

  3. Spring 框架的@Scheduled注解:如果你在使用 Spring 框架,可以利用@Scheduled注解来简化定时任务的配置。Spring 的调度器会根据注解的参数来执行相应的方法。

  4. Quartz Scheduler:这是一个开源的作业调度库,提供了比 Java 标准库更强大的定时任务功能。Quartz 允许你配置复杂的调度策略,如 cron 表达式,并支持集群。

java.util.Timer

利用 Spring 框架支持相对来说比较常见。下面我写了一个 java.util.Timer 实现每秒打印一次时间的定时任务的简单案例。可以按以下步骤编写代码:


  1. 创建一个继承自TimerTask的类,在其中实现run方法。

  2. 创建一个Timer对象。

  3. 使用Timer对象的schedule方法安排任务。


以下是具体的示例代码:



package com.funtest.temp;

import com.funtester.frame.SourceCode;
import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;
public class FunTester extends SourceCode {
public static void main(String[] args) { Timer timer = new Timer();// 实例化Timer类 TimerTask task = new TimerTask() { @Override public void run() {// 实例化TimerTask类 // 任务代码,打印当前时间,并指定线程名称 SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); Date date = new Date(System.currentTimeMillis()); System.out.println(Thread.currentThread().getName() + " " + formatter.format(date)); } }; // 0表示立即执行,1000表示每隔1秒执行一次 timer.scheduleAtFixedRate(task, 0, 1000); }
}
复制代码


下面是控制台打印的信息:


Timer-0      10:04:07Timer-0      10:04:08Timer-0      10:04:09Timer-0      10:04:10
复制代码


使用 java.util.Timer 实现定时任务,虽然具有简单易用优点,但在我的经验范围内极少,大多数都是包装成服务化,使用 Spring 自带的定时任务执行。其主要缺点就是:单线程执行、异常处理不够优雅、不支持并发。总体来讲不如 ScheduledExecutorService 功能强大。

ScheduledExecutorService

下面是 ScheduledExecutorService 的简单案例。


package com.funtest.temp;      import com.funtester.frame.SourceCode;    import java.text.SimpleDateFormat;  import java.util.Date;  import java.util.concurrent.Executors;  import java.util.concurrent.ScheduledExecutorService;  import java.util.concurrent.TimeUnit;    public class FunTester extends SourceCode {        public static void main(String[] args) {          // 创建一个具有单个线程的调度程序          ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);          // 创建一个Runnable任务,每秒打印一次当前时间          Runnable task = new Runnable() {              @Override              public void run() {                  SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");                  System.out.println(Thread.currentThread().getName() + "      " + sdf.format(new Date()));              }          };          // 从现在开始1秒钟之后,每隔1秒钟执行一次          scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);      }    }
复制代码


明显看到用到了线程池的使用,有点不用多说,简洁性我感觉不输 java.util.Timer

Go 语言定时任务

Go 语言定时任务实现,也是比较简单的也是利用 Go SDK 自带的 time 包。下面是简单的案例:



package main
import ( "fmt" "time")
func main() { // 创建一个定时器,每隔1秒钟执行一次 ticker := time.NewTicker(1 * time.Second) // 延迟关闭定时器 defer ticker.Stop() // 无限循环 for { // 从定时器的通道中接收数据 select { case t := <-ticker.C: fmt.Println("Current time:", t) } }}
复制代码


这个案例基本是最佳实现,如果大家有需求都可以参考,而且 Go 语言 goroutine 的原因,不用很关心协程性能的问题,我自己就直接使用 goroutine 直接起定时任务了。这种对于复杂的定时任务,这种方式就显得力不从心了。


Go 语言 time 包里面还有一种方法实现:


package main    import (      "fmt"      "time")    func main() {      // 创建一个定时器,1秒后触发      timer := time.AfterFunc(1*time.Second, func() {         fmt.Println("Current time:", time.Now())      })        // 防止程序退出      select {      case <-timer.C:      }  }
复制代码


语法上看好像区别不是很大,各位自己选用吧。

cron 包

下面分享一下 Go 第三方包的实现。cron 是一个在 Go 语言中实现定时任务的流行库,它允许你使用 cron 表达式来定义任务的执行时间。PS:这个 cron 目测只支持了分钟级别的。


package main    import (      "fmt"      "time"      "github.com/robfig/cron/v3")    func main() {      // 创建一个定时任务      c := cron.New()      // 添加一个定时任务,每隔1分钟执行一次      c.AddFunc("0/1 * * * *", func() {         fmt.Println("Current time:", time.Now())      })      // 启动定时任务      c.Start()      // 防止程序退出      select {}  }
复制代码


AddFunc 方法参数 spec 不仅支持 cron 语法,为了方便使用还增加了一个 @every 语法,后面可以跟类似 @every 1m2stime.ParseDuration() 支持的格式都可以用在这里。除此之外 cron 预定义了一些时间规则:


  • @yearly:也可以写作@annually,表示每年第一天的 0 点。等价于0 0 1 1 *

  • @monthly:表示每月第一天的 0 点。等价于0 0 1 * *

  • @weekly:表示每周第一天的 0 点,注意第一天为周日,即周六结束,周日开始的那个 0 点。等价于0 0 * * 0

  • @daily:也可以写作@midnight,表示每天 0 点。等价于0 0 * * *

  • @hourly:表示每小时的开始。等价于0 * * * *

gocron 包

还有一个更加灵活的库 gocron 功能更加强大,但使用起来存在一定门槛。下面是一个案例:


package main
import ( "fmt" "time"
"github.com/go-co-op/gocron")
func main() { // 定时任务 s := gocron.NewScheduler(time.Local) // 每秒执行一次 s.Every(1).Minutes().Do(func() { fmt.Println("Current time:", time.Now()) }) s.StartBlocking() fmt.Println("任务结束")}
复制代码


gocron 设置时间的话,都可以通过调用不同的 API 来组装实现。对于 API 学习需要一定的学习成本。同时 gocron 也支持 cron 语法的。下面是个例子:


package main
import ( "fmt" "time"
"github.com/go-co-op/gocron")
func main() { // 定时任务 s := gocron.NewScheduler(time.Local) // 添加一个每周二上午10点每分钟执行一次的任务,使用 cron 表达式 s.Cron("0 * 10 ? * 2").Do(func() { fmt.Println("Task executed at:", time.Now()) }) s.StartBlocking() fmt.Println("任务结束")}
复制代码

发布于: 刚刚阅读数: 4
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
Java & Go 定时任务_FunTester_InfoQ写作社区