zoukankan      html  css  js  c++  java
  • Java 定时任务

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


    1. 定时任务

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


    使用定时任务的情况:

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






    2. Thread实现

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


    2.1 使用

    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 使用

    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 分析

    // 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 使用

    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

    @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 依赖

    <!-- xxl-job-core -->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
        <version>${project.parent.version}</version>
    </dependency>
    

    6.3.2 基本配置

    # 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 定时任务

    @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



  • 相关阅读:
    循环语句 for , forin , forEach
    Haxe
    Haxe数据类型
    jango中间件的使用
    python的遍历模式
    python文件读写
    python3.5以上自带的虚拟环境在各种环境下的使用方法
    冒泡排序
    工厂方法模式
    redis 配置
  • 原文地址:https://www.cnblogs.com/Howlet/p/15580411.html
Copyright © 2011-2022 走看看