定时任务实现方案总结

news2024/11/17 13:22:34

一、概述

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

本文重点说明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() {
            @Override
            public 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 {

    @Bean
    public 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 的任务调度器
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        //可配置两种类型:TaskScheduler、ScheduledExecutorService
        //scheduledTaskRegistrar.setScheduler(taskScheduler());
        //只可配置一种类型:taskScheduler
        scheduledTaskRegistrar.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.coloradmin.cn/o/984215.html

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

相关文章

基于SSM的房屋租售网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

2023如何做谷歌收录?

答案是&#xff1a;2023谷歌收录可以通过GPC爬虫池技术完成。 搜索引擎优化&#xff08;SEO&#xff09;对于任何网站来说都是至关重要的。 特别是谷歌作为全球最大的搜索引擎&#xff0c;网站是否能被其快速收录直接关系到网站的流量和影响力。 以下是关于2023年如何做谷歌…

nginx请求接口转发-浏览器访问80端口,要把请求转发至8882

1、需求 浏览器访问80端口&#xff0c;要把请求转发至8882 2、实现 修改ngixn配置文件 ngin配置文件在nginx安装目录/nginx/conf文件夹下 cd /usr/local/nginx/confvi ngin.conf修改server配置 server {listen 80;server_name localhost;location / {proxy_pass …

【MySQL】JDBC 编程详解

JDBC 编程详解 一. 概念二. JDBC 工作原理三. JDBC 使用1. 创建项目2. 引入依赖3. 编写代码(1). 创建数据源(2). 建立数据库连接(3). 创建 SQL(4). 执行 SQL(5). 遍历结果集(6). 释放连接 4. 完整的代码5. 如何不把 sql 写死 &#xff1f;6. 获取连接失败的情况 四. JDBC常用接…

关于假冒我司关联公司进行欺诈活动的严正声明!

近日&#xff0c;有客户致电我司&#xff0c;反馈其在力软的“关联公司”进行了产品咨询时&#xff0c;对方声称可以以更低的价格出售力软次级品牌低代码产品。 对此&#xff0c;我司经过调查&#xff0c;确认所谓“关联公司”系冒充&#xff0c;我司对此类冒用我司名义进行的…

山洪、地质灾害监测利器-泥石流、山体滑坡AI视觉仪

1、设备介绍 AI视觉仪通过AI算法智能化摄像机&#xff0c;能够及时、全面的把握边坡潜在安全风险&#xff0c;有效防范自然灾害。支持全天候运行&#xff0c;在恶劣环境及气候条件下仍能正常进行监测数据采集。自动识别监控区域内是否有泥石流、山体滑坡等&#xff0c;一旦检测…

java中使用 Integer 和 int 的 含义、使用方法 及之间的区别

学习目标&#xff1a; 学习目标如下&#xff1a; 明确 Integer 和 int 的 含义、使用方法 及之间的区别 学习内容&#xff1a; 一、区别&#xff1a; 1.Integer是int的包装类&#xff0c;int则是java的一种基本的数据类型&#xff1b; 2.Integer变量必须实例化之后才能使用&a…

python爬取网站图片案例

下载指定页的图片Get方式 # _*_ coding : utf-8 _*_ # Time : 2023/9/6 9:50 # Author : xiaoyu # File : 爬取表情包 # Project : basicimport urllib.request from lxml import etreedef get_request(page):if (page 1):url "https://sc.chinaz.com/tupian/keaitupia…

【C++ Core Guidelines解析】C++学习之路的一盏明灯

前言&#xff1a;C语言的功能非常丰富&#xff0c;表达能力非常强。因为一种成功的通用编程语言拥有的功能必须比任何开发人员所需要的更多&#xff0c;任何一种有生命力且不断发展的语言都会不断积累用于表达程序员思想的替代用法。这会导致选择过载。那么&#xff0c;开发人员…

本地电脑搭建web服务器、个人博客网站并发布公网访问 【无公网IP】(1)

文章目录 前言1. 安装套件软件2. 创建网页运行环境 指定网页输出的端口号3. 让WordPress在所需环境中安装并运行 生成网页4. “装修”个人网站5. 将位于本地电脑上的网页发布到公共互联网上 前言 在现代社会&#xff0c;网络已经成为我们生活离不开的必需品&#xff0c;而纷繁…

【react】Hooks原理和实战

前言 在最初学习react的时候&#xff0c;我们大部分会选择去扒一扒React的官方文档&#xff0c;看看他是什么&#xff0c;怎么使用的。而我却很好奇在文档里学习的第一个完整的组件是 类&#xff08;Class&#xff09;组件&#xff0c;但是在实际工作中我们看到项目中所声明的…

无涯教程-JavaScript - ERFC.PRECISE函数

描述 ERFC.PRECISE函数返回x和无穷大之间集成的互补ERF函数。 互补误差函数等于1-ERF(即1-误差函数),由等式给出- $$Erfc(x) \frac {2} {\sqrt {\pi}} \int_ {x} ^ {\infty} e ^ {-t ^ 2} dt $$ 语法 ERFC.PRECISE(x)争论 Argument描述Required/OptionalxThe lower bound…

K8s 多集群实践思考和探索

作者&#xff1a;vivo 互联网容器团队 - Zhang Rong 本文主要讲述了一些对于K8s多集群管理的思考&#xff0c;包括为什么需要多集群、多集群的优势以及现有的一些基于Kubernetes衍生出的多集群管理架构实践。 一、为什么需要多集群 随着K8s和云原生技术的快速发展&#xff0c…

如何优雅的实现一个Mybatis插件

定位&#xff1a;此篇尝试用另一种角度描述如何完成一个mybatis插件&#xff0c;全程可以按段落跳跃阅读&#xff0c;有任何不适欢迎指出Thanks♪(&#xff65;ω&#xff65;)&#xff89; 为什么这么设计 如果我想实现一个orm增强插件&#xff0c;首先就应该避免硬编码&…

C#写一个UDP程序判断延迟并运行在Centos上

服务端 using System.Net.Sockets; using System.Net;int serverPort 50001; Socket server; EndPoint client new IPEndPoint(IPAddress.Any, 0);//用来保存发送方的ip和端口号CreateSocket();void CreateSocket() {server new Socket(AddressFamily.InterNetwork, SocketT…

低代码是程序员“玩”出来的

一、前言 所谓“低代码”&#xff0c;最开始的雏形是程序员写一些重复的东西写腻了&#xff0c;产品今天想加个请假表&#xff0c;明天加个物资申请表&#xff0c;后天又想统计一下记录等等要求。对程序员来说开发这些就是一个个没意思的重复开发工作&#xff0c;所以就想着搞个…

LVGL Animations(动画)的简单使用

一、前言 哈喽&#xff0c;大家好。在进行界面设计的时候&#xff0c;动画的使用是必不可少的&#xff0c;今天这篇文章就跟大家分享一下 LVGL Animations&#xff08;动画&#xff09;的简单使用。笔者将在模拟器上运行演示&#xff0c;LVGL 版本号为 8.3.0。 二、Animation…

【HTML专栏3】!DOCTYPE、lang、字符集的作用

本文属于HTML/CSS专栏文章&#xff0c;适合WEB前端开发入门学习&#xff0c;详细介绍HTML/CSS如果使用&#xff0c;如果对你有所帮助请一键三连支持&#xff0c;对博主系列文章感兴趣点击下方专栏了解详细。 博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;HTML/CS…

当AI遇到IoT:开启智能生活的无限可能

文章目录 1. AI和IoT的融合1.1 什么是人工智能&#xff08;AI&#xff09;&#xff1f;1.2 什么是物联网&#xff08;IoT&#xff09;&#xff1f;1.3 AI和IoT的融合 2. 智能家居2.1 智能家居安全2.2 智能家居自动化 3. 医疗保健3.1 远程监护3.2 个性化医疗 4. 智能交通4.1 交通…

Json“牵手”易贝商品详情数据方法,易贝商品详情API接口,易贝API申请指南

易贝是一个可让全球民众在网上买卖物品的线上拍卖及购物网站&#xff0c;易贝&#xff08;EBAY&#xff09;于1995年9月4日由Pierre Omidyar以Auctionweb的名称创立于加利福尼亚州圣荷塞。人们可以在易贝上通过网络出售商品。2014年2月20日&#xff0c;易贝宣布收购3D虚拟试衣公…