定时任务实现方案总结

一、概述

定时任务的作用是在设定的时间和日期后自动执行任务,执行任务的周期既能是单次也能是周期性。

本文重点说明Timer、ScheduledThreadPoolExecutor、Spring Task、Quartz等几种定时任务技术方案。

image-20230907162017206

二、Timer

JDK自带的Timer是最古老的定时任务实现方式了。Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以安排任务“执行一次”或者定期“执行多次”。

在实际的开发当中,经常需要一些周期性的操作,比如每5分钟执行某一操作等。对于这样的操作最方便、高效的实现方式就是使用java.util.Timer工具类。

核心方法:

// 在指定延迟时间后执行指定的任务
schedule(TimerTask task,long delay);// 在指定时间执行指定的任务。(只执行一次)
schedule(TimerTask task, Date time);// 延迟指定时间(delay)之后,开始以指定的间隔(period)重复执行指定的任务
schedule(TimerTask task,long delay,long period);// 在指定的时间开始按照指定的间隔(period)重复执行指定的任务
schedule(TimerTask task, Date firstTime , long period);// 在指定的时间开始进行重复的固定速率执行任务
scheduleAtFixedRate(TimerTask task,Date firstTime,long period);// 在指定的延迟后开始进行重复的固定速率执行任务
scheduleAtFixedRate(TimerTask task,long delay,long period);// 终止此计时器,丢弃所有当前已安排的任务。
cancal()// 从此计时器的任务队列中移除所有已取消的任务。
purge()

总结:

(1)Timer的方法整体可以分为按延时时间和日期时间两种执行任务,其参数分别对应long delay、Date time;

(2)其中schedule()方法是按照固定间隔来定时执行任务,而scheduleAtFixedRate()方法是按照固定速率来定时执行任务的,他们的区别是如果任务执行时间比较长,已经执行到下一个周期了,schedule()方法执行的任务错过了就错过了,而scheduleAtFixedRate()方法则会努力赶上,保障周期内的任务执行速率固定;

代码示例

/*** @author yangnk* @desc* @date 2023/09/06 23:00**/
public class TimerTest {public static void main(String[] args) {Timer timer = new Timer();Date date = new Date();System.out.println("before date = " + date.toString());timer.schedule(new TimerTask() {@Overridepublic void run() {Date date = new Date();System.out.println("after date = " + date.toString());System.out.println("task thread name = " + Thread.currentThread().getName());}}, 3000);}
}

原理:Timer做定时任务的原理是使用的Object.wait(timeout),来进行的线程阻塞实现的,他的实现是单线程模式。

三、ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

ScheduledThreadPoolExecutor的层级结果如下:

img

核心方法:

ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);<V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);

总体来说,ScheduledThreadPoolExecutor中实现的方法和Timer差不多,都能够实现按照延时和日期来执行任务,也区分按照固定延时时间和固定速率来执行任务。但和Timer的最大区别是ScheduledThreadPoolExecutor的定时任务是是多线程执行的,每开始执行一个定时任务,他就会从线程池中取一个线程来执行,这样的话他就不存在一个定时任务延期影响后一个定时任务的情况了。他的原理是使用 DelayQueue 作为延时任务队列,等时间到了再创建工作线程执行。

JDK原生的定时任务实现方式Timer和ScheduledThreadPoolExecutor最大的问题是它不支持cron表达式和持久化机制,这个在下面的Spring Task和Quartz中得到了解决。

四、Spring Task

从Spring 3开始,Spring自带了一套定时任务工具Spring Task,可以把它看成是一个轻量级的Quartz,使用起来十分简单,除Spring相关的包外不需要额外的包,支持注解和配置文件两种形式。通常情况下在Spring体系内,针对简单的定时任务,可直接使用Spring提供的功能。

在项目实践中通常是在需要做定时任务的方法上添加@Scheduled注解,并在启动类上添加@EnableScheduling注解。

代码实现

//任务实现/*** @author yangnk* @desc* @date 2023/09/07 15:11**/
@Service
@Slf4j
public class SpringTaskTest {/*** 使用Cron表达式,每3s执行一次*/@Scheduled(cron = "0/3 * * * * ? ")public void job1() {log.info("cron job, the time is now {}", new Date());}/*** 延迟3s执行*/@Scheduled(fixedDelay = 3000L)public void job2() {log.info("fixedDelay job, the time is now {}", new Date());}/*** 延迟3s执行,按照规定速率*/@Scheduled(fixedRate = 3000L)public void job3() {log.info("fixedRate job, the time is now {}", new Date());}
}

Spring Task底层是基于ThreadPoolTaskScheduler来实现,可以自定义线程池的大小等参数,只需要实现SchedulingConfigurer接口即可。

//定时任务线程池配置类/*** @author yangnk* @desc* @date 2023/09/07 15:24**/
@Configuration
public class TaskConfig implements SchedulingConfigurer {@Beanpublic TaskScheduler taskScheduler() {ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();executor.setPoolSize(10);executor.setThreadNamePrefix("my-task-thread");//设置饱和策略//CallerRunsPolicy:线程池的饱和策略之一,当线程池使用饱和后,直接使用调用者所在的线程来执行任务;如果执行程序已关闭,则会丢弃该任务executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}//配置@Scheduled 定时器所使用的线程池//配置任务注册器:ScheduledTaskRegistrar 的任务调度器@Overridepublic void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {//可配置两种类型:TaskScheduler、ScheduledExecutorService//scheduledTaskRegistrar.setScheduler(taskScheduler());//只可配置一种类型:taskSchedulerscheduledTaskRegistrar.setTaskScheduler(taskScheduler());}}

总结:Spring task支持延时下发任务和cron定时执行任务,但Spring task 本身不支持持久化,也没有推出官方的分布式集群模式,只能靠开发者在业务应用中自己手动扩展实现,无法满足可视化,易配置的需求。

五、Quartz

参考本人所写的《分布式定时任务框架Quartz总结和实践》系列文章。

六、Linux Cron

Linux Cron也是一种非常普遍的实现定时任务的方式,实际是操作系统的定时任务。Linux Cron只能到达分钟级,到不了秒级别。

这个经常会用在Linux定时备份和巡检相关业务。

七、时间轮

时间轮是一种算法思想,在Kafka、Dubbo、ZooKeeper、Netty 、Caffeine 、Akka 中都有对时间轮的实现。

时间轮简单来说就是一个环形的队列(底层一般基于数组实现),队列中的每一个元素(时间格)都可以存放一个定时任务列表。

时间轮中的每个时间格代表了时间轮的基本时间跨度或者说时间精度,加入时间一秒走一个时间格的话,那么这个时间轮的最高精度就是 1 秒(也就是说 3 s 和 3.9s 会在同一个时间格中)。

时间轮比较适合任务数量比较多的定时任务场景,它的任务写入和执行的时间复杂度都是 0(1)。

img

八、总结

TimerScheduledThreadPoolExecutorSpring TaskQuartzLinux Cron
优点JDK原生自带,简单轻便JDK原生自带,简单轻便Spring框架实现,和springboot集成非常简单,支持cron表达式Quartz功能非常丰富,支持cron表达式、支持持久化和分布式部署Linux原生自带,简单便捷
缺点单线程实现,不支持cron表达式和持久化机制线程池实现,不支持cron表达式和持久化机制不支持持久化机制框架比较重,需要依赖外部组件,有较高的耦合性功能单一,不保障可靠性

参考资料

  1. Java中定时任务的6种实现方式,你知道几种?:https://juejin.cn/post/6992719702032121864#heading-16 (主要参考)
  2. Java 定时任务框架大揭秘!| JavaGuide:https://zhuanlan.zhihu.com/p/414296662 (时间轮算法)
  3. Java之旅–定时任务(Timer、Quartz、Spring、LinuxCron):https://www.kancloud.cn/digest/java-travel/159427 (linux cron)

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

博物馆网上展厅有哪些用途,如何搭建数字时代的文化宝库

引言&#xff1a; 博物馆一直以来都是保存、展示和传承人类文化遗产的地方。然而&#xff0c;随着数字时代的来临&#xff0c;博物馆也逐渐迎来了创新的变革。博物馆网上展厅&#xff0c;作为一种新型的文化传播方式&#xff0c;正逐渐崭露头角。 一.什么是博物馆网上展厅&…

【目标检测】理论篇(3)YOLOv5实现

Yolov5网络构架实现 import torch import torch.nn as nnclass SiLU(nn.Module):staticmethoddef forward(x):return x * torch.sigmoid(x)def autopad(k, pNone):if p is None:p k // 2 if isinstance(k, int) else [x // 2 for x in k] return pclass Focus(nn.Module):def …

《C++设计模式》——结构型

前言 结构模式可以让我们把很多小的东西通过结构模式组合起来成为一个打的结构&#xff0c;但是又不影响各自的独立性&#xff0c;尽可能减少各组件之间的耦合。 Adapter Class/Object(适配器&#xff09; Bridge(桥接&#xff09; Composite(组合) Decorator(装饰) 动态…

【5】openGL使用宏和函数进行错误检测

当我们编写openGL程序&#xff0c;没有报编译链接错误&#xff0c;但是运行结果是黑屏&#xff0c;这不是我们想要的。 openGL提供了glGetError 来检查错误&#xff0c;我们可以通过在运行时进行打断点查看glGetError返回值&#xff0c;得到的是一个十进制数&#xff0c;将其转…

C++(Liunx) 使用cut截 取出Ubuntu用户的家目录,要求:不能使用“:“作为分割.

使用cut截 取出Ubuntu用户的家目录&#xff0c;要求&#xff1a;不能使用":"作为分割

【C++技能树】多态解析

Halo&#xff0c;这里是Ppeua。平时主要更新C&#xff0c;数据结构算法&#xff0c;Linux与ROS…感兴趣就关注我bua&#xff01; 文章目录 0.多态的概念0.1 多态的定义 1. 重写2.Final与Override3.抽象类4.多态中的内存分布.4.1虚表存在哪里? 5.多态调用原理5.1 动态绑定与静…

《向量数据库指南》——AI原生向量数据库Milvus Cloud 2.3 Enhancement

Enhancement MMap 技术提升数据容量 MMap 是 Linux 内核提供的技术,可以将一块磁盘空间映射到内存,这样一来我们便可以通过将数据加载到本地磁盘再将磁盘 mmap 到内存的方案提升单机数据的容量,经过测试使用 MMap 技术后数据容量提升了 1 倍而性能下降在20% 以内,大大节约了…

sqlibs安装及复现

sqlibs安装 安装phpstudy后&#xff0c;到github上获取sqlibs源码 sqli-labs项目地址—Github获取&#xff1a;GitHub - Audi-1/sqli-labs: SQLI labs to test error based, Blind boolean based, Time based. 在phpstudy本地文件中的Apache目录中解压上方下载的源码。 将sq…

08.SCA-CNN

目录 前言泛读摘要IntroductionRelated Work 精读Spatial and Channel-wise Attention CNNOverviewSpatial AttentionChannel-wise AttentionChannel-SpatialSpatial-Channel ExperimentsDataset and Metric设置 评估Channel-wise Attention&#xff08;问题1&#xff09;评估M…

momentjs实现DatePicker时间禁用

momentjs是一个处理时间的js库&#xff0c;简洁易用。 浅析一下&#xff0c; momentjs 在vue中对DatePicker时间组件的禁用实践。 一&#xff0c;npm下载 npm install moment --save二&#xff0c;particles.json中 "dependencies": {"axios": "^…

单片机第三季-第一课:STM32基础

官方网址&#xff1a;STMCU中文官网 STM32系列分类&#xff1a; 型号命名原则&#xff1a; STM32F103系列&#xff1a; 涉及到的几个概念&#xff1a; DMA&#xff1a;Direct Memory Access&#xff0c;直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间&…

系统学习Linux-zabbix监控平台

一、zabbix的基本概述 zabbix是一个监控软件&#xff0c;其可以监控各种网络参数&#xff0c;保证企业服务架构安全运营&#xff0c;同时支持灵活的告警机制&#xff0c;可以使得运维人员快速定位故障、解决问题。zabbix支持分布式功能&#xff0c;支持复杂架构下的监控解决方…

DataTable扩展 列转行方法(2*2矩阵转换)

源数据 如图所示 // <summary>/// DataTable扩展 列转行方法&#xff08;2*2矩阵转换&#xff09;/// </summary>/// <param name"dtSource">数据源</param>/// <param name"columnFilter">逗号分隔 如SDateTime,PM25,PM10…

【QT】使用qml的QtWebEngine遇到的一些问题总结

在使用qt官方的一些QML的QtWebEngine相关的例程的时候&#xff0c;有时在运行会报如下错误&#xff1a; WebEngineContext used before QtWebEngine::initialize() or OpenGL context creation failed 这个问题在main函数里面最前面加上&#xff1a; QCoreApplication::setAttr…

Linux下的系统编程——认识进程(七)

前言&#xff1a; 程序是指储存在外部存储(如硬盘)的一个可执行文件, 而进程是指处于执行期间的程序, 进程包括 代码段(text section) 和 数据段(data section), 除了代码段和数据段外, 进程一般还包含打开的文件, 要处理的信号和CPU上下文等等.下面让我们开始对Linux进程有个…

利用transform和border 创造简易图标,以适应uniapp中多字体大小情况下的符号问题

heml: <text class"icon-check"></text> css: .icon-check {border: 2px solid black;border-left: 0;border-top: 0;height: 12px;width: 6px;transform-origin: center;transform: rotate(45deg);} 实际上就是声明一个带边框的div 将其中相邻的两边去…

java八股文面试[数据库]——主键的类型自增还是UUID

auto_increment的优点&#xff1a; 字段长度较uuid小很多&#xff0c;可以是bigint甚至是int类型&#xff0c;这对检索的性能会有所影响。 在写的方面&#xff0c;因为是自增的&#xff0c;所以主键是趋势自增的&#xff0c;也就是说新增的数据永远在后面&#xff0c;这点对于…

Android之 SVG绘制

一 SVG介绍 1.1 SVG&#xff08;Scalable Vector Graphics&#xff09;是可缩放矢量图形的缩写&#xff0c;它是一种图形格式&#xff0c;其中形状在XML中指定&#xff0c; 而XML又由SVG查看器呈现。 1.2 SVG可以区别于位图&#xff0c;放大可以做到不模糊&#xff0c;可以做…

Vagrant + VirtualBox + CentOS7 + WindTerm 5分钟搭建本地linux开发环境

1、准备阶段 将环境搭建所需要的工具和文件下载好&#xff08;页面找不到可参考Tips部分&#xff09; Vagrant 版本&#xff1a;vagrant_2.2.18_x86_64.msi 链接&#xff1a;https://developer.hashicorp.com/vagrant/downloads VirtualBox 版本&#xff1a;VirtualBox-6.1.46…

无涯教程-JavaScript - DAYS360函数

描述 DAYS360函数返回基于360天的年份(十二个月为30天)的两个日期之间的天数,该天数用于会计计算。 语法 DAYS360 (start_date,end_date,[method])争论 Argument描述Required/OptionalStart_dateThe two dates between which you want to know the number of days.Required…