前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 定时任务

Java 定时任务

作者头像
晚上没宵夜
发布2021-11-24 10:00:50
2.9K0
发布2021-11-24 10:00:50
举报

还没真正的遇到使用定时任务的场景,不管怎么说先学起来

1. 定时任务

很多情况下任务并非需要立即执行,而是需要往后或定期执行,这不可能人工去操作,所以定时任务就出现了。项目中肯定会用到使用定时任务的情况,笔者就需要定时去拉取埋点数据

使用定时任务的情况:

  • 每周末凌晨备份数据
  • 触发条件 5 分钟后发送邮件通知
  • 30 分钟未支付取消订单
  • 每 1 小时去拉取数据
  • ......

2. Thread实现

笔试中首次遇到定时任务急急忙忙想出来的方法

2.1 使用

代码语言:javascript
复制
public class ThreadSchedule {
    public static void main(String[] args) {
      
        // 5 秒后执行任务
        int interval = 1000 * 5;

        // 新线程执行
        new Thread(() -> {
            try {
                Thread.sleep(interval);
                System.out.println("执行定时任务");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

2.2 分析

  • 定时不准确,因依赖底层硬件,Windows误差为10微妙
  • System.currentTimeMillis() 依赖系统硬件,还会受网络时间同步修改
  • System.nanoTime() 依赖 JVM 的运行纳秒数,并不受同步影响,适用于计算准确的时间差
  • 但计算当前日期还是要使用 currentTimeMillis 的格林威治时间,而 nanoTime 计算 JVM 运行时间不准确

3. java.util.Timer

Timer 负责执行计划功能,会启动一个后台线程,而 TimerTask 负责任务逻辑

3.1 使用

代码语言:javascript
复制
public class TimeSchedule {
    public static void main(String[] args) {
        
        // 启动一个守护线程
        Timer timer = new Timer();  
        
        // 定时任务
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行定时任务");
            }
        };

        long delay = 0;     		// 延迟执行
        long period = 1000 * 5;  	// 间隔
        timer.scheduleAtFixedRate(timerTask, 1, period);
        // timer.cancel();			   可取消
    }
}

3.2 分析

代码语言:javascript
复制
// new Timer() 最终的构造函数会启动一个线程
public Timer(String name) {
    thread.setName(name);
    thread.start();
}

// 这个线程里面封装了一个 Queue 优先级队列,该线程会去队列里不停执行里面的任务
class TimerThread extends Thread {
    private TaskQueue queue;
    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }
}

// 这个队列里面存放了各种 TimerTask 定时的任务逻辑
class TaskQueue {
    private TimerTask[] queue = new TimerTask[128];
}
  • 只有一个单线程执行,所以是串行执行
  • 某个任务执行时间较长会阻塞后面预定执行的任务,所以时间并不准确
  • 线程报错后续的定时任务直接停止

4. ScheduledExecutorService

java.util.concurrent中的工具类,是一个多线程的定时器

4.1 使用

代码语言:javascript
复制
public class ExecutorSchedule {
    public static void main(String[] args) {
        
        // 定时任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("执行定时任务");
            }
        };

        // 线程池执行
        long delay = 0;     		// 延迟执行
        long period = 1000 * 5;  	// 间隔
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS);
}

笔者最常用的一个定时操作了,之前还写过定时的探测任务

5. Spring的定时任务

需要开启定时功能@EnableScheduling

代码语言:javascript
复制
@Component
public class SpringSchedule {
    
    // cron 表达式,每秒执行一次
    
    @Scheduled(cron = "*/1 * * * * ?")
    public void springSchedule(){
        System.out.println("执行定时任务");
    }
    
}

底层是 ScheduledThreadPoolExecutor 线程池,和上面的 ScheduledExecutorService 是同根同源

6. XXL-JOB

xxl-job 是个人维护的分布式任务调度框架(国人写的,有详细的中文文档),分为 调度中心 和 执行器。执行器就是定时任务,而调度中心则负责管理调用这些定时任务,调度中心也可以存储定时任务通过脚本形式(Java 是 Grovvy)免编译地实时下发到各服务中执行。最重要的是有 UI 界面,用户友好的体验

6.1 建立数据库

xxl-job 的存储是基于数据库的,相对比 quartz 可保存在内存和数据库有一点性能影响。首先第一步就是要建库,在 xxl-job 官网有 SQL 语句 tables_xxl_job.sql,直接执行即可建库建表

6.2 部署 xxl-job-admin 调度中心

从 Git 上拉取最新的代码,然后编译根模块,填好 admin 模块的数据库地址等,即可启动这个调度中心(支持 Docker 部署,更加方便)

6.3 创建定时任务

在需要定时任务的服务中 引入依赖、添加配置、创建定时任务

6.3.1 依赖
代码语言:javascript
复制
<!-- xxl-job-core -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>${project.parent.version}</version>
</dependency>
6.3.2 基本配置
代码语言:javascript
复制
# xxl-job admin address
xxl.job.admin.addresses=http://xxx.xxx.xxx:8080/xxl-job-admin

# xxl-job executor appname
xxl.job.executor.appname=xxl-job-executor-demo
6.3.3 定时任务
代码语言:javascript
复制
@Component
public class MyJob {
    
    @XxlJob("MyJob")
    public void MyJob() throws Exception {
        
        // 执行器日志记录
        XxlJobHelper.log("myjob is execute");
        
        // 定时任务逻辑
        System.out.println("myjob is executing");
        
        // default success
    }
}

6.4 执行定时任务

进入调度中心新建一个任务,然后执行定时任务即可(使用的是 RPC 远程过程调用)

6.5 遇到的问题

默认执行器是自动注册到调度中心的,但是时常进去的地址有问题而导致执行失败,所以要手动录入执行器的地址

6.6 分析

作为轻量级的分布式定时任务,有 UI 界面简单方便使用,而且对代码没什么侵入性,已经能满足大部分项目的需求了,笔者如果要用定时任务也会首选 xxl-job

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-11-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 定时任务
  • 2. Thread实现
    • 2.1 使用
      • 2.2 分析
      • 3. java.util.Timer
        • 3.1 使用
          • 3.2 分析
          • 4. ScheduledExecutorService
            • 4.1 使用
            • 5. Spring的定时任务
            • 6. XXL-JOB
              • 6.1 建立数据库
                • 6.2 部署 xxl-job-admin 调度中心
                  • 6.3 创建定时任务
                    • 6.3.1 依赖
                    • 6.3.2 基本配置
                    • 6.3.3 定时任务
                  • 6.4 执行定时任务
                    • 6.5 遇到的问题
                      • 6.6 分析
                      相关产品与服务
                      数据库
                      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档