Java 定时任务


摘自:https://www.cnblogs.com/Howlet/p/15580411.html

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



 
分类:  架构设计
好文要顶  关注我  收藏该文   
1
0
 
 
 
« 上一篇:  SpringBoot Actuator — 埋点和监控
» 下一篇:  Java的脚本机制、编译器API
posted @  2021-11-20 11:00  Howlet  阅读(242)  评论(0)  编辑  收藏  举报

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM