定时任务的几种实现方式

定时任务实现的几种方式:

1、JDK自带

  • (1)Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
  • (2)ScheduledExecutorService:也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

2、Spring Task

  • Spring Task:Spring3.0以后自带的task,Spring Boot提供的用于定时任务控制的注解@Scheduled注解,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

3、第三方任务调度框架

除了使用Spring框架提供的 @Scheduled 注解和SchedulingConfigurer接口外,还有许多第三方的任务调度库可供选择。这些库通常提供了更多的功能和灵活性,以满足各种复杂的任务调度需求。以下是一些常见的第三方任务调度库:

  • Quartz Scheduler:是一个功能强大且灵活的任务调度库,具有丰富的功能,如支持基于cron表达式的任务调度、集群支持、作业持久化等。它可以与Spring框架集成,并且被广泛应用于各种类型的任务调度应用程序中。
  • xxl-job:是一个分布式任务调度平台,提供了可视化的任务管理界面和多种任务调度方式,如单机任务、分布式任务、定时任务等。它支持任务执行日志、任务失败重试、动态调整任务执行策略等功能。
  • Elastic Job:是一个分布式任务调度框架,可以轻松实现分布式任务调度和作业执行。它提供了分布式任务执行、作业依赖关系、作业分片等功能,适用于大规模的分布式任务调度场景。
  • PowerJob:是一个开源的分布式任务调度框架,由阿里巴巴集团开发并开源。PowerJob 提供了分布式、高可用的任务调度能力,支持多种任务类型,如定时任务、延时任务、流程任务等。

目录

一、JDK自带

1、使用Timer

2、使用ScheduledExecutorService

二、Spring Task

1、基础使用案例 

2、参数说明简介

3、cron表达式

(1)cron参数配置描述

(2)cron通配符描述

4、cron案例

5、实现多任务并行(默认单线程)

6、局限性(不支持年份设定)


一、JDK自带

1、使用Timer

/*** Timer:这是java自带的java.util.Timer类** 这个类允许你调度一个java.util.TimerTask任务* 使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行* 【一般用的较少】*/
@Configuration
public class JDKTimerTask {@Beanpublic void test(){TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.err.println("task  run:"+ LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());}};Timer timer = new Timer();//安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次timer.schedule(timerTask,10,3000);}
}

执行结果

2、使用ScheduledExecutorService

/*** ScheduledExecutorService:也jdk自带的一个类;* 是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行* 也就是说,任务是并发执行,互不影响。*/
@Configuration
public class ScheduledExecutorServiceTimerTask {@Beanpublic void test(){ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();// 参数:1、任务体 2、首次执行的延时时间//      3、任务执行间隔 4、间隔时间单位service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+ LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName()), 0, 3, TimeUnit.SECONDS);}
}

执行结果:

二、Spring Task

@Scheduled注解是Spring Boot提供的用于定时任务控制的注解,主要用于控制任务在某个指定时间执行,或者每隔一段时间执行

@Scheduled需要配合@EnableScheduling使用。使用时,将@Scheduled注解放在待定时的方法名上方,将 @EnableScheduling放在项目主启动类类名上方。@Scheduled主要有三种配置执行时间的方式:cronfixedRate 和 fixedDelay

注意:@Scheduled是不支持年份设置,spring quartz支持

1、基础使用案例 

(1)主启动类增加注解@EnableScheduling:表明开启定时任务 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling //开启定时任务注解
public class TimedTaskDemoApplication {public static void main(String[] args) {SpringApplication.run(TimedTaskDemoApplication.class, args);}}

(2)定时任务执行方法上增加注解@Scheduled:表明该方法为定时任务需要执行的内容 

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;@Component
@Slf4j
public class TaskDemo {//    @Scheduled(cron = "0 0 4 ? * SAT")//每周六凌晨4:00@Scheduled(cron = "* * * * * *")//每秒执行一次private void doTask(){log.info("执行定时任务:" + new Date());}
}

2、参数说明简介

参数说明
cron 任务执行的cron表达式
zonecron表达时解析使用的时区,默认为服务器的本地时区。
使用java.util.TimeZone#getTimeZone(String)方法解析
GMT-8:00
fixedRate固定速率
上一次任务执行开始到下一次执行开始的间隔时间固定,单位为ms。
若在调度任务执行时,上一次任务还未执行完毕,会加入worker队列,等待上一次执行完成后,马上执行下一次任务
1000
fixedRateString与fixedRate一致,只是间隔时间使用java.time.Duration#parse解析1000或PT1S
fixedDelay 固定延迟
上一次任务执行结束到下一次执行开始的间隔时间固定,单位为ms。
1000
fixedDelayString与fixedDelay一致,只是间隔时间使用java.time.Duration#parse解析1000或PT1S
initialDelay 首次延迟多长时间后执行,单位ms。
之后按照fixedRate、fixedRateString、fixedDelay、fixedDelayString指定的规则执行,需要指定其中一个规则。
注意:不能和cron一起使用
1000
initialDelayString 与initialDelay 一致,只是间隔时间使用java.time.Duration#parse解析 1000或PT1S

关于 fixedRate 和 fixedDelay的区别:

fixedRate 的间隔时间是上次任务开始后,开始计算时间间隔,达到指定时间间隔后,开始执行下一次任务

fixedDelay 的间隔时间是上次任务结束后,开始计算时间间隔,达到指定时间间隔后,开始执行下一次任务

例如:当一个任务需要统计大量数据,并根据不用用户生成不同文件报表,并将生成的报表推送到相应的用户下,该任务有补充机制。在执行该任务时,将使用大量时间时,此时使用 fixedRate 就会造成工作队列长时间堆积,同时,如果使用 fixedRate 的话,与 cron 的作用高度重合,基本可以用 cron 表达式替代,这个情况下,使用 fixedDelay 效果更好,这是在一个任务结束后,再次执行该任务进行重试,直到所有用户都拿到相应的文件。

3、cron表达式

 cron@Scheduled的一个参数,是一个字符串,以空格隔开

@Scheduled(cron = "{秒数} {分钟} {小时} {日期} {月份} {星期}")

(1)cron参数配置描述

单位允许值允许通配符
0-59,  -  *  / 
分钟0-59,  -  *  / 
小时0-23,  -  *  / 
日期1-31,  -  *  /  ?  L  W
月份1-12或JAN-DEC(大小写均可),  -  *  /  ?
星期

1-7或SUN-SAT(大小写均可)

注:星期日为每周第一天,所以1-7表示周末到周六

,  -  *  /  ?  L  #

(2)cron通配符描述

符号含义
*所有值,在秒字段上表示每秒执行,在月字段上表示每月执行
不指定值,不需要关心当前指定的字段的值,比如每天都执行但不需要关心周几就可以把周的字段设为?
-区间或者范围,如秒的0-2 ,表示0秒、1秒、2秒都会触发
,指定值,比如在0秒、20秒、25秒触发,可以把秒的字段设为0,20,25
/递增触发,比如秒的字段上设0/3 ,表示从第0秒开始,每隔3秒触发
L最后,只允许在日字段或周字段上,在日字段上使用L表示当月最后一天,在周字段上使用3L表示该月最后一个周二
W只允许用在日字段上,表示距离最近的该日的工作日,工作日指的是周一至周五
#只允许在周字段上,表示每月的第几个周几,如2#3 , 每月的第3个周二

说明: 

在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟

在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样

“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值

当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”

“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写

但是它在两个子表达式里的含义是不同的。

在天(月)子表达式中,“L”表示一个月的最后一天

在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT

如果在“L”前有具体的内容,它就具有其他的含义了

例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五

注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题

4、cron案例

"0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点"0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时"0 0 12 ? * WED" 表示每个星期三中午12点"0 0 12 * * ?" 每天中午12点触发"0 15 10 ? * *" 每天上午10:15触发"0 15 10 * * ?" 每天上午10:15触发"0 15 10 * * ? *" 每天上午10:15触发"0 15 10 * * ? 2005" 2005年的每天上午10:15触发"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发"0 15 10 15 * ?" 每月15日上午10:15触发"0 15 10 L * ?" 每月最后一日的上午10:15触发"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

5、实现多任务并行(默认单线程)

默认情况下如果存在多个定时任务方法是单线程同步执行

验证代码:创建两个定时任务,其中一个睡眠3秒钟

@Component
@Slf4j
public class TaskDemo {@Scheduled(cron = "* * * * * *")//每秒执行一次private void doTask(){try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info("执行定时任务1:" + new Date());}@Scheduled(cron = "* * * * * *")//每秒执行一次private void doTask2(){log.info("执行定时任务【2】:" + new Date());}
}

单线程验证结果:从执行时间和线程名可以看出两个定时任务是单线程,使用的是同一个线程执行的

增加配置类:异步任务配置类进行线程池的设置

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;import java.util.concurrent.Executor;/*** 异步任务配置类* 需要实现SchedulingConfigurer接口* 需要实现AsyncConfigurer接口*/
@Configuration
public class AsynTaskConfig implements AsyncConfigurer,SchedulingConfigurer {// 定义池子的容量private static final int CRON_POOL_SIZE = 10;// 注册定时任务线程池@Beanpublic ThreadPoolTaskScheduler getThreadPoolTaskScheduler() {ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();// 初始化线程池threadPoolTaskScheduler.initialize();// 设置池子容量threadPoolTaskScheduler.setPoolSize(CRON_POOL_SIZE);return threadPoolTaskScheduler;}@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setTaskScheduler(getThreadPoolTaskScheduler());}//重写AsyncConfigurer的两个方法(异步的配置)@Overridepublic Executor getAsyncExecutor() {return AsyncConfigurer.super.getAsyncExecutor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();}
}

异步多线程验证结果:从执行时间和线程名可以看出两个定时任务是多线程

6、局限性(不支持年份设定)

@Scheduled 的 cron 无法指定执行的年份

spring taks 不支持年位定时,它毕竟不是quartz,只是简单的定时框架,比起jdk Timer就加入了线程池而以.

一旦制定到年份,会存在问题,启动项目的时候,会一直报一个错误,大概的意思是你的定时任务将永远不会被执行,导致项目一直启动不了。

错误场景一:年份设置在第7位时报错,cron只支持到六位

错误场景二:年份设置在第6位时报错,cron第六位表示星期,内容范围只支持1-7

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

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

相关文章

Unreal游戏GPU参数详解,游戏性能优化再升级

UWA GOT Online For Unreal GPU模式近期全新发布,方便开发者从渲染和带宽的角度进行GPU分析。同时,此次更新中UWA也增加了丰富的GPU参数,涵盖了GPU SoC和GPU Counter模块。这些新增的参数不仅能够帮助Unreal开发者从宏观层面监控GPU的压力状况…

Python爬虫--Urllib基础

1. urlretrieve Urllib 库也是类似 request 库,用来解析html的 首先讲 urlretrieve 子模块 这个模块的作用是将网页下载到本地 语法: urlretrieve(网址,本地地址) 例如: 这样就可以了,他会将百度网页下载到本地D盘下&#x…

怎么给切面增加开关

为切面添加开关功能,可以在运行时控制是否启用切面逻辑,这对于调试、性能优化或特定场景的灵活控制非常有用。下面是一个基于Spring AOP和配置属性实现的简单示例,展示了如何为切面逻辑添加开关。 步骤 1: 添加配置属性 首先,定…

【管理咨询宝藏93】大型制造集团数字化转型设计方案

【管理咨询宝藏93】大型制造集团数字化转型设计方案 【格式】PDF版本 【关键词】国际咨询公司、制造型企业转型、数字化转型 【核心观点】 - 235页大型制造型集团数字化转型方案设计!细节非常详尽,图表丰富! - 系统架构必须采用成熟、具有国…

Kafka应用Demo:按主题订阅消费消息

安装环境 Kafka安装可参考官方网站的指导(https://kafka.apache.org/quickstart), 按步骤解压压缩包,修改配置。然后再启动zookeeper和kafka-server即可。 需要注意的一点:如果是在VMware虚拟机上启动的kafka, 需要修改一下server.properties配置文件&am…

STM32G0存储器和总线架构

文章目录 前言一、系统架构二、存储器构成三、存储器地址映射四、存储器边界地址五、外设寄存器边界地址 前言 此文章是STM32G0 MCU的学习记录,并非权威,请谨慎参考。 STM32G0主流微控制器基于工作频率可达64 MHz的高性能Arm Cortex-M0 32位RISC内核。该…

观测云 VS ELK:谁是日志监控的王者?

前言 作为 IT 信息系统运行状态感知和故障分析的重要手段,日志在行业兴起之初便为运维和开发环节所广泛应用。当应用和系统发生故障或出现问题时,日志数据成为了排查和诊断问题的重要依据。通过分析日志,开发人员和运维人员可以了解系统的运…

(python读取目标检测PASCAL VOC数据集,将图像采用边缘填充的方法缩放到相应大小(例如640x640),并修改对应的xml文件

python读取目标检测PASCAL VOC数据集,将图像采用边缘填充的方法缩放到相应大小(例如640x640),并修改对应的xml文件 单个文件处理批处理脚本 单个文件处理 import cv2 import os import xml.etree.ElementTree as ETdef resize_im…

苹果审核遇到的问题总结

问题一 Guideline 2.3.3 - Performance - Accurate Metadata 2.3.3 Screenshots should show the app in use, and not merely the title art, login page, or splash screen. They may also include text and image overlays (e.g. to demonstrate input mechanisms, such a…

java JMH 学习

JMH 是什么? JMH(Java Microbenchmark Harness)是一款专用于代码微基准测试的工具集,其主要聚焦于方法层面的基准测试,精度可达纳秒级别。此工具由 Oracle 内部负责实现 JIT 的杰出人士编写,他们对 JIT 及…

#LLM入门 | langchain | RAG # 4.1 简介_使用 LangChain 访问个人数据

课程介绍:《第四部分:使用 LangChain 访问个人数据》是基于LangChain创始人Harrison Chase与Deeplearning.ai合作开发的课程,旨在教授如何通过LangChain框架使语言模型(LLM)访问和应用用户个人数据。背景说明&#xff…

PD芯片取电:电子设备的动力之源6020 6500

随着现代电子技术的迅猛发展,电源管理技术在各种电子设备中扮演着越来越重要的角色。特别是近年来,随着USB Power Delivery(PD)技术的普及,PD芯片取电技术因其高效、灵活和安全的特点,成为了电子设备充电和…

java项目中如何更优雅的处理空值

在Java项目中,优雅地处理空值(null)是一个重要的实践,因为它有助于提高代码的健壮性和可读性。以下是一些建议,帮助你更优雅地处理空值: 使用Optional类(Java 8及以上) Java 8引入了…

Vue + Element-plus 快速入门

1. 构建项目 npm init vuelatest # 可选项一路回车,使用默认NO,按提示执行3条命令 cd 项目名 npm install npm run dev 2. 下载element-plus npm install element-plus --save 3.替换main.js import { createApp } from vue import ElementPlus from element-plu…

相关性分析

目录 1.交叉功率谱 2. 相关系数 1.交叉功率谱 % 生成两个信号 t 0:0.001:100; x sin(2*pi*1*t)sin(2*pi*2*t); y sin(2*pi*t )sin(2*pi*2*t); % 计算交叉功率谱密度 [Pxy, F] cpsd(x, y, [], [], [], 1/(t(2)-t(1))); % 使用正确的采样频率 % 绘制交叉功率谱密度图 …

ISIS的基本配置

1.IS-IS协议的基本配置(1) 2.IS-IS协议的基本配置(2) 3.IS-IS协议的基本配置(3) 4.案例:IS-IS配置 R1的配置如下: [AR1czy]isis 1 [AR1czy-isis-1]is-level level-1 [AR1czy-isis-…

电磁兼容(EMC):静电放电(ESD)基本原理

目录 1. 静电学简史 2. 摩擦生电原理 3. 总结 静电放电是电磁兼容(EMC)系列里最让人头疼的问题之一。无论是现在还是未来,静电问题肯定是做产品设计需要重点考虑的问题。这里来聊聊关于静电放电的一些发展历程和基本原理。 1. 静电学简史…

公开地图服务

XYZ参考: http://openwhatevermap.xyz/ 高德参考:https://blog.csdn.net/ldlzhy1984/article/details/81015180 https://blog.csdn.net/fredricen/article/details/77189453 高德矢量图:https://webrd02.is.autonavi.com/appmaptile?langzh_cn&size…

市面上好用的AI工具有哪些?

市面上的AI工具数不胜数,选择合适自己的AI工具则需要考虑自己的需求,看是否能满足的使用需求。那么市面上又有哪些好用的AI工具呢? 泰迪智能科技拥有简单易用的大数据挖掘建模平台,能够让数据创造更大的价值。 功能板块&…

技术干货:单体,SOA,微服务,分布式,集群架构详解

理解这些架构概念对于软件工程师至关重要。下面我会简要介绍这些架构,并提供一些示例代码来说明它们的实现方式。 单体架构: 在单体架构中,整个应用程序被构建为一个单独的、紧密耦合的单元。通常使用传统的三层架构:表示层、业务…