iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java -- 定时任务实现方式
  • 447
分享到

Java -- 定时任务实现方式

JavaQuartzScheduledXxl-Job 2023-09-09 16:09:06 447人浏览 泡泡鱼
摘要

在Java开发中,定时任务是一种十分常见的功能. 定时任务是在约定时间内执行的一段程序 如每天凌晨24点备份同步数据,又或者电商平台 30 分钟后自动取消未支付的订单,每隔一个小时拉取一次数据等都需要使用到定时器 批量处理数据:批量统计上个

在Java开发中,定时任务是一种十分常见的功能.

定时任务是在约定时间内执行的一段程序

如每天凌晨24点备份同步数据,又或者电商平台 30 分钟后自动取消未支付的订单,每隔一个小时拉取一次数据等都需要使用到定时器

  • 批量处理数据:批量统计上个月的某个数据。
  • 时间驱动的场景:某个时间点发送短信、邮件。
  • 固定频率的场景:每隔5分钟需要执行一次

在Java中,实现定时任务的方式有很多,最简单的在线程中通过JDK自带TimerThread.sleep睡眠线程,或者采用SpringBoot中的@Schedule注解,或者采用定时线程池ScheduledExecutorService来实现,又或者采用Spring Boot中集成Quartz框架实现

一、Thread线程等待(最原始最简单方式)

创建一个thread,然后让它在while循环里一直运行着,通过sleep方法来达到定时任务的效果

匿名内部类实现 java.lang.Runnable 接口

public class ThreadTask {    public static void main(String[] args) {        final long timeInterval = 1000;        //创建线程(匿名内部类方式)        Thread thread = new Thread(new Runnable() {            @Override            public void run() {                while (true){                    SimpleDateFORMat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                    String dateStr = sdf.format(new Date());                    System.out.println("线程等待实现定时任务:" + dateStr);                    try {                        Thread.sleep(timeInterval);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        //开启线程        thread.start();    }}

自定义类实现 java.lang.Runnable 接口

public class ThreadTask1 {    public static void main(String[] args) {        //自定义类实现java.lang.Runnable接口        MyRunnable runnable = new MyRunnable();        //创建线程(自定义类MyRunnable实现java.lang.Runnable接口)        Thread t = new Thread(runnable);        //开启线程        t.start();    }}class MyRunnable implements Runnable{    final long timeInterval = 1000;    @Override    public void run() {        while (true){            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");            String dateStr = sdf.format(new Date());            System.out.println("线程等待实现定时任务1:" + dateStr);            try {                Thread.sleep(timeInterval);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

二、Timer(最古老方式)

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

public class MyTimerTask {    public static void main(String[] args) {        // 定义一个定时任务        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                String dateStr = sdf.format(new Date());                System.out.println("运行定时任务:" + dateStr);            }        };        // 计时器        Timer timer = new Timer();        // 添加执行任务(延迟 1s 执行,每 3s 执行一次)        timer.schedule(timerTask, 1000, 3000);    }}

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();

Timer 缺点分析

1、任务执行时间长影响其他任务

当一个任务的执行时间过长时,会影响其他任务的调度

public class MyTimerTask1 {    public static void main(String[] args) {        // 定时任务1        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                String dateStr = sdf.format(new Date());                System.out.println("进入定时任务1:" + dateStr);                try {                    // 休眠 5 秒                    TimeUnit.SECONDS.sleep(5);                } catch (InterruptedException e) {                    e.printStackTrace();                }                dateStr = sdf.format(new Date());                System.out.println("运行定时任务1:" + dateStr);            }        };        // 定时任务2        TimerTask timerTask2 = new TimerTask() {            @Override            public void run() {                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                String dateStr = sdf.format(new Date());                System.out.println("运行定时任务2:" + dateStr);            }        };        // 计时器        Timer timer = new Timer();        // 添加执行任务(延迟 1s 执行,每 2s 执行一次)        timer.schedule(timerTask, 1000, 2000);        timer.schedule(timerTask2, 1000, 2000);    }}

当任务 1 运行时间超过设定的间隔时间时,任务 2 也会延迟执行 原本任务 1 和任务 2 的执行时间间隔都是 2s,但因为任务 1 执行了 5s,因此任务 2 的执行时间间隔也变成了 10s(和原定时间不符)

2、任务异常影响其他任务

使用 Timer 类实现定时任务时,当一个任务抛出异常,其他任务也会终止运行

Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,它会错误的认为整个Timer线程都会取消。同时,已经被安排单尚未执行的TimerTask也不会再执行了,新的任务也不能被调度

public class MyTimerTask2 {    public static void main(String[] args) {        // 定时任务1        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                String dateStr = sdf.format(new Date());                System.out.println("进入定时任务1:" + dateStr);                //发生异常                int num = 10 / 0;                dateStr = sdf.format(new Date());                System.out.println("运行定时任务1:" + dateStr);            }        };        // 定时任务2        TimerTask timerTask2 = new TimerTask() {            @Override            public void run() {                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");                String dateStr = sdf.format(new Date());                System.out.println("运行定时任务2:" + dateStr);            }        };        // 计时器        Timer timer = new Timer();        // 添加执行任务(延迟 1s 执行,每 2s 执行一次)        timer.schedule(timerTask, 1000, 2000);        timer.schedule(timerTask2, 1000, 2000);    }}

Timer 小结

Timer 类实现定时任务的优点是方便,因为它是 JDK 自定的定时任务,但缺点是任务如果执行时间太长或者是任务执行异常,会影响其他任务调度

三、ScheduledExecutorService;ScheduledThreadPool

ScheduledExecutorService 是JAVA 1.5后新增的定时任务接口,它是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行(任务是并发执行,互不影响)

ScheduledExecutorService 可以实现Timer具备的所有功能,并解决了 Timer类存在的问题

注:

只有当执行调度任务时,ScheduledExecutorService才会真正启动一个线程,其余时间ScheduledExecutorService都是出于轮询任务的状态

public class MyScheduledExecutorService {    public static void main(String[] args) {        // 创建任务队列;10为线程数量        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);        //执行任务        scheduledExecutorService.scheduleAtFixedRate(() -> {            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");            String dateStr = sdf.format(new Date());            System.out.println("ScheduledExecutorService执行定时任务:" + dateStr);        },1,2, TimeUnit.SECONDS);// 1s 后开始执行,每 2s 执行一次    }}

public class ScheduledThreadPool {    public static void main(String[] args) {        // 参数代表可以同时执行的定时任务个数        ScheduledExecutorService service = Executors.newScheduledThreadPool(3);                service.schedule(() -> {            System.out.println("task0-start");            sleep(2);            System.out.println("task0-end");        }, 2, TimeUnit.SECONDS);                service.scheduleAtFixedRate(() -> {            System.out.println("task1-start");            sleep(2);            System.out.println("task1-end");        }, 1, 2, TimeUnit.SECONDS);                service.scheduleWithFixedDelay(() -> {            System.out.println("task2-start");            sleep(2);            System.out.println("task2-end");        }, 1, 2, TimeUnit.SECONDS);    }    private static void sleep(long time) {        try {            TimeUnit.SECONDS.sleep(time);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

ScheduledExecutorService主要有以下4个方法

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

scheduleAtFixedRatescheduleWithFixedDelay在实现定时程序时比较方便,运用的也比较多。

ScheduledExecutorService中定义的这四个接口方法和Timer中对应的方法几乎一样,只不过Timerscheduled方法需要在外部传入一个TimerTask的抽象任务。 而ScheduledExecutorService封装的更加细致了,传RunnableCallable内部都会做一层封装,封装一个类似TimerTask的抽象任务类(ScheduledFutureTask)。然后传入线程池,启动线程去执行该任务

scheduleAtFixedRate方法

public ScheduledFuture scheduleAtFixedRate(Runnable command,    long initialDelay,    long period,    TimeUnit unit);

command为被执行的线程;initialDelay为初始化后延时执行时间;period为两次开始执行最小间隔时间;unit为计时单位

scheduleWithFixedDelay方法

public ScheduledFuture scheduleWithFixedDelay(Runnable command,    long initialDelay,    long delay,    TimeUnit unit);

command为被执行的线程;initialDelay为初始化后延时执行时间;period为前一次执行结束到下一次执行开始的间隔时间(间隔执行延迟时间);unit为计时单位

ScheduledExecutorService;ScheduledThreadPool小结

在单机生产环境下建议使用 ScheduledExecutorService 来执行定时任务,它是 JDK 1.5 之后自带的 API,因此使用起来也比较方便,并且使用 ScheduledExecutorService 来执行任务,不会造成任务间的相互影响

四、Spring Task;@Scheduled

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

如果使用的是 Spring 或 Spring Boot 框架,可以直接使用 Spring Framework 自带的定时任务,使用上述定时任务的实现方式,很难实现设定了具体时间的定时任务,如当我们需要每周五来执行某项任务时,但如果使用 Spring Task 就可轻松的实现

以 Spring Boot 为例,实现定时任务只需两步:

开启定时任务

添加定时任务

1、开启定时任务

如果是在Spring Boot项目中,需要在启动类上添加@EnableScheduling来开启定时任务

@EnableScheduling // 开启定时任务@SpringBootApplicationpublic class Job4ScheduledApplication {    public static void main(String[] args) {        SpringApplication.run(Job4ScheduledApplication.class, args);    }}

2、添加定时任务

定时任务的添加只需要使用 @Scheduled 注解标注即可,如果有多个定时任务可以创建多个 @Scheduled 注解标注的方法

@Component  //@Component用于实例化类,将其类托管给 Spring 容器public class TaskJobUtil {        @Scheduled(cron = "0/2 * * * * ?")    public void task() {        System.out.println("task0-start");        sleep(5);        System.out.println("task0-end");    }        @Scheduled(fixedRate = 2000)    public void task0() {        System.out.println("task0-start");        sleep(5);        System.out.println("task0-end");    }        @Scheduled(fixedDelay = 2000)    public void task1() {        System.out.println("task1-start");        sleep(5);        System.out.println("task1-end");    }        @Scheduled(initialDelay = 2000, fixedDelay = 3000)    public void task2() {        System.out.println("task2-start");        sleep(5);        System.out.println("task2-end");    }    private void sleep(long time) {        try {            TimeUnit.SECONDS.sleep(time);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

CronTrigger(Cron触发器)功能非常强大,是基于日历的作业调度,而SimpleTrigger是精准指定间隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表达式的

Cron 表达式是一个字符串,以5或6个空格隔开,分为6或7个域,每一个域代表一个含义

Cron的表达式被用来配置CronTrigger实例

从左到右分别为:秒、分、时、日期、月份、星期几、年份

具体参考博文: 

https://blog.csdn.net/MinggeQingchun/article/details/125865778

Cron 在线生成 

crontab执行时间计算 - 在线工具 

quartz/Cron/Crontab表达式在线生成工具-BeJSON.com

[秒] [分] [时] [日期] [月] [星期] [秒] [分] [时] [日期] [月] [星期] [年]

*:表示任何时间触发任务 ,

:表示指定的时间触发任务

-:表示一段时间内触发任务

/:表示从哪一个时刻开始,每隔多长时间触发一次任务。

?:表示用于月中的天和周中的天两个子表达式,表示不指定值 

常用表达式例子

  (1)0/2 * * * * ?   表示每2秒 执行任务

  (1)0 0/2 * * * ?    表示每2分钟 执行任务

  (1)0 0 2 1 * ?   表示在每月的1日的凌晨2点调整任务

  (2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业

  (3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作

  (4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点 

  (5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时 

  (6)0 0 12 ? * WED    表示每个星期三中午12点 

  (7)0 0 12 * * ?   每天中午12点触发 

  (8)0 15 10 ? * *    每天上午10:15触发 

  (9)0 15 10 * * ?     每天上午10:15触发 

  (10)0 15 10 * * ?    每天上午10:15触发 

  (11)0 15 10 * * ? 2005    2005年的每天上午10:15触发 

  (12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发 

  (13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发 

  (14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 

  (15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发 

  (16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发 

  (17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发 

  (18)0 15 10 15 * ?    每月15日上午10:15触发 

  (19)0 15 10 L * ?    每月最后一日的上午10:15触发 

  (20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发 

  (21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发 

  (22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发

fixedDelay和fixedRate的区别 

fixedRate有一个时刻表的概念,在任务启动时,T1、T2、T3就已经排好了执行的时刻,比如1分、2分、3分,当T1的执行时间大于1分钟时,就会造成T2晚点,当T1执行完时T2立即执行。

fixedDelay比较简单,表示上个任务结束,到下个任务开始的时间间隔。无论任务执行花费多少时间,两个任务间的间隔始终是一致的

注:

Spring Task 本身不支持持久化,也没有推出官方的分布式集群模式,只能靠开发者在业务应用中自己手动扩展实现,无法满足可视化,易配置的需求

基于Spring Task实现定时任务

  • 优点:

    • 不需要依赖外部框架。
    • 简单快速实现任务。@EnableScheduling@Scheduled 注解
  • 缺点:

    • 无法管理任务。要停止某个任务,必须重新发布。
    • 不支持动态调整。修改任务参数需要重启项目。
    • 不支持集群方式部署。集群模式下会出现任务多次被调度执行的情况,因为集群的节点之间是不会共享任务信息的,每个节点上的任务都会按时执行

五、Quartz

除了JDK自带的API之外,我们还可以使用开源的框架来实现,比如Quartz

Quartz是Job scheduling(作业调度)领域的一个开源项目,Quartz既可以单独使用也可以跟spring框架整合使用,在实际开发中一般会使用后者。使用Quartz可以开发一个或者多个定时任务,每个定时任务可以单独指定执行的时间,例如每隔1小时执行一次、每个月第一天上午10点执行一次、每个月最后一天下午5点执行一次等。

Quartz架构图如下:

Quartz通常有三部分组成:调度器(Scheduler)、任务(JobDetail)、触发器(Trigger,包括SimpleTriggerCronTrigger

1、Job

定义具体要执行的任务

2、JobDetail

配置要执行任务的描述信息,即如何去定位要执行的Job,每次执行任务时,都会根据JobDetail创建一个Job对象,避免任务并发执行时访问同一个Job对象产生问题

jobdetail 就是对job的定义,而job是具体执行的逻辑内容。 具体的执行的逻辑需要实现 job类,并实现execute方法。如果使用jobdetail来定义,那么每次调度都会创建一个new job实例,这样带来的好处就是任务并发执行的时候,互不干扰,不会对临界资源造成影响

3、Trigger

触发器,配置任务执行的时间规则,需要和一个JobDetail关联起来

在 Quartz 中,trigger 是用于定义 Job 何时执行。当用 Scheduler 注册一个 Job 的时候要创建一个 Trigger 与这个 Job 相关联。

Quartz 中主要提供了四种类型的 Trigger:包括SimpleTrigger、CronTirgger//DateIntervalTrigger和 NthIncludedDayTrigger。这四种 trigger 可以满足企业应用中的绝大部分需求。

最常用的是 SimpleTrigger 和 CronTrigger

一般来说,如果你需要在一个固定的时间和重复次数或者一个固定的间隔时间,那么SimpleTrigger 比较合适; 如果你有许多复杂的作业调度,那么 CronTrigger 比较合适。

CronTrigger 和 Unix 的 cron 机制基本一样,基于通用的公历,我们需要的只是熟悉cron 表达式的用法。

关于Quartz中时间表达式的设置—–corn表达式:

withIdentity() 给触发器一些属性 比如名字,组名

startNow() 立刻启动

withSchedule(ScheduleBuilder schedBuilder) 以某种触发器触发

usingJobData(String dataKey, Boolean value) 给具体job传递参数

4、Scheduler

调度器,它维护了一个JobDetailTrigger的注册表,当任务关联的触发器到达预定的时间,调度器会去执行任务

Scheduler代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。

Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。 Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行,例如:如schedulerTest.scheduleJob(jobTest, triggerTest)。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。

Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例。

scheduler 由 scheduler 工厂创建:包括DirectSchedulerFactory 和 StdSchedulerFactory(STD:standard标准的意思)。 第二种工厂 StdSchedulerFactory 使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。

scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。

scheduler 除了启动外,scheduler 操作包括查询、设置 scheduler 为 standby 模式、继续、停止。 启动scheduler 非常简单,只需要调用 start() 方法即可。只有在scheduler 有实例或standby 模式才能调用start() 方法,一旦调用shutdown() 方法之后就不能再调用start() 方法。

(1)在Spring Boot中集成Quartz需要先添加如下Maven依赖

    org.springframework.boot    spring-boot-starter-quartz    2.6.6

(2)在启动类添加@EnableScheduling注解来开启定时任务 

(3)配置文件quartz.properties

#主要分为scheduler、threadPool、jobStore、dataSource等部分org.quartz.scheduler.instanceId=AUTOorg.quartz.scheduler.instanceName=DefaultQuartzScheduler#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略org.quartz.scheduler.rmi.export=false#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099org.quartz.scheduler.rmi.proxy=falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction=false#实例化ThreadPool时,使用的线程类为SimpleThreadPoolorg.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool#threadCount和threadPriority将以setter的形式注入ThreadPool实例#并发个数  如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.#只有1到100之间的数字是非常实用的org.quartz.threadPool.threadCount=5#优先级 默认值为5org.quartz.threadPool.threadPriority=5#可以是“true”或“false”,默认为falseorg.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)org.quartz.jobStore.misfireThreshold=5000# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore#持久化方式,默认存储在内存中,此处使用数据库方式org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和sql语句)来执行其工作的委托,用于完全符合JDBC的驱动程序org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题org.quartz.jobStore.useProperties=true#表前缀org.quartz.jobStore.tablePrefix=QRTZ_#数据源别名,自定义org.quartz.jobStore.dataSource=qzDS#使用阿里的druid作为数据库连接池org.quartz.dataSource.qzDS.connectionProvider.class=org.example.config.DruidPoolinGConnectionProviderorg.quartz.dataSource.qzDS.URL=jdbc:Mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTCorg.quartz.dataSource.qzDS.user=rootorg.quartz.dataSource.qzDS.passWord=123456org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driverorg.quartz.dataSource.qzDS.maxConnections=10#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏#org.quartz.jobStore.isClustered=false

或者添加定时任务配置类

@Configurationpublic class ScheduleConfig{    @Bean    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)    {        SchedulerFactoryBean factory = new SchedulerFactoryBean();        factory.setDataSource(dataSource);        // quartz参数        Properties prop = new Properties();        prop.put("org.quartz.scheduler.instanceName", "ZM Schedule");        prop.put("org.quartz.scheduler.instanceId", "AUTO");        // 线程池配置        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");        prop.put("org.quartz.threadPool.threadCount", "20");        prop.put("org.quartz.threadPool.threadPriority", "5");        // JobStore配置        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");        // 集群配置        prop.put("org.quartz.jobStore.isClustered", "false");        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");        // sqlserver 启用        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");        prop.put("org.quartz.jobStore.misfireThreshold", "12000");        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");        factory.setQuartzProperties(prop);        factory.setSchedulerName("ZMScheduler");        // 延时启动        factory.setStartupDelay(1);        factory.setApplicationContextSchedulerContexTKEy("applicationContextKey");        // 可选,QuartzScheduler        // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了        factory.setOverwriteExistingJobs(true);        // 设置自动启动,默认为true        factory.setAutoStartup(true);        return factory;    }}

关于配置详细解释:任务调度框架Quartz(五)Quartz任务调度框架之最全Quartz系统参数配置详解_青山师的博客-CSDN博客

也可查看官网:

Quartz Documentation

1、定义Job

定义Job有两种方式,
第一种是直接定义任务类,并注册到Spring ioc容器中:

@Servicepublic class QuartzJobService {    public void taskJob(){        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");        String dateStr = sdf.format(new Date());        System.out.println("job0-start:" + dateStr);        try {            TimeUnit.SECONDS.sleep(5);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("job0-end:" + dateStr);    }}

第二种是继承QuartzJobBean,重写executeInternal方法,这种方式可以接受JobDetail传递的参数

public class QuartzJobDetail extends QuartzJobBean {    @Override    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");        String dateStr = sdf.format(new Date());        System.out.println("job1-start:" + dateStr);        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        // 获取参数        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();        String date = jobDataMap.getString("date");        System.out.println("参数:" + date);        System.out.println("job1-end:" + dateStr);    }}

这样就把JobDetail和我们之前定义的QuartzJob关联起来了。

2、配置JobDetail

JobDetail可以使用MethodInvokingJobDetailFactoryBean或者JobDetailFactoryBean配置,配置工作需要在一个Spring配置类中完成,可以定义一个QuartzConfig配置类,首先看MethodInvokingJobDetailFactoryBean的使用:

@Configurationpublic class QuartzConfig {        //JobDetail可以使用MethodInvokingJobDetailFactoryBean配置    @Bean    public MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean(){        MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();        // 指定任务类在IoC容器中的Bean名称        bean.setTargetBeanName("quartzJobService");        // 指定要执行的方法名称        bean.setTargetMethod("taskJob");        return bean;    }}

这样就把JobDetail和之前QuartzJob所定义的任务关联起来了,接下来看JobDetailFactoryBean

@Configurationpublic class QuartzCronTriggerConfig {    //JobDetail可以使用JobDetailFactoryBean配置    @Bean    public JobDetailFactoryBean jobDetailFactoryBean() {        JobDetailFactoryBean bean = new JobDetailFactoryBean();        // 指定任务类名称        bean.setJobClass(QuartzJobDetail.class);        // 准备参数        JobDataMap jobDataMap = new JobDataMap();        jobDataMap.put("date", "2020-08-08");        // 传递参数        bean.setJobDataMap(jobDataMap);        return bean;    }}

这样就把JobDetail和之前定义的QuartzJob2关联起来了,同时传递了参数。

3、配置Trigger

Trigger同样定义在QuartzConfig配置类里,常用的TriggerSimpleTriggerCronTrigger等,它们分别可以通过SimpleTriggerFactoryBeanCronTriggerFactoryBean来完成配置,我们先用SimpleTriggerFactoryBean配置的触发器关联MethodInvokingJobDetailFactoryBean配置的JobDetail

@Configurationpublic class QuartzConfig {    @Bean    public SimpleTriggerFactoryBean simpleTriggerFactoryBean() {        SimpleTriggerFactoryBean bean = new SimpleTriggerFactoryBean();        bean.setRepeatCount(10);        bean.setRepeatInterval(2000);        // 关联JobDetailbean.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());        return bean;    }}

SimpleTriggerFactoryBean的用法比较简单
再用CronTriggerFactoryBean配置的触发器关联JobDetailFactoryBean配置的JobDetail

@Configurationpublic class QuartzConfig {    @Bean    public CronTriggerFactoryBean cronTriggerFactoryBean() {        CronTriggerFactoryBean bean = new CronTriggerFactoryBean();        bean.setCronExpression("0/2 * * * * ? 2020");        // 关联JobDetail        bean.setJobDetail(jobDetailFactoryBean().getObject());        return bean;    }}

CronTriggerFactoryBean可以实现类似Spring中@Scheduledcron表达式的功能,同时支持了年份的配置。

4、配置Scheduler

最后一步就是通过SchedulerFactoryBean来配置Scheduler,来注册Trigger

@Configurationpublic class QuartzConfig {    @Bean    public SchedulerFactoryBean schedulerFactoryBean() {        SchedulerFactoryBean bean = new SchedulerFactoryBean();        // 注册两个Trigger        bean.setTriggers(simpleTriggerFactoryBean().getObject(), cronTriggerFactoryBean().getObject());        return bean;    }}

Quartz会使用异步线程去执行定时任务,不会出现像@Scheduled中定时任务在同一线程同步执行的情况。

或者编写了Configuration 配置类

@Configurationpublic class QuartzSimpleTriggerConfig {        //JobDetail可以使用MethodInvokingJobDetailFactoryBean配置    @Bean    public MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean(){        MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();        // 指定任务类在IoC容器中的Bean名称        bean.setTargetBeanName("quartzJobService");        // 指定要执行的方法名称        bean.setTargetMethod("taskJob");        return bean;    }    //通过SimpleTriggerFactoryBean配置的触发器关联MethodInvokingJobDetailFactoryBean配置的JobDetail    @Bean    public SimpleTriggerFactoryBean simpleTriggerFactoryBean() {        SimpleTriggerFactoryBean bean = new SimpleTriggerFactoryBean();        bean.setRepeatCount(10);        bean.setRepeatInterval(2000);        // 关联JobDetail        bean.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());        return bean;    }        //通过SchedulerFactoryBean来配置Scheduler,来注册Trigger    @Bean    public SchedulerFactoryBean schedulerFactoryBean() {        SchedulerFactoryBean bean = new SchedulerFactoryBean();        // 注册两个Trigger        bean.setTriggers(simpleTriggerFactoryBean().getObject());        return bean;    }}
@Configurationpublic class QuartzCronTriggerConfig {    //JobDetail可以使用JobDetailFactoryBean配置    @Bean    public JobDetailFactoryBean jobDetailFactoryBean() {        JobDetailFactoryBean bean = new JobDetailFactoryBean();        // 指定任务类名称        bean.setJobClass(QuartzJobDetail.class);        // 准备参数        JobDataMap jobDataMap = new JobDataMap();        jobDataMap.put("date", "2020-08-08");        // 传递参数        bean.setJobDataMap(jobDataMap);        return bean;    }        //通过CronTriggerFactoryBean配置的触发器关联MethodInvokingJobDetailFactoryBean配置的JobDetail    @Bean    public CronTriggerFactoryBean cronTriggerFactoryBean() {        CronTriggerFactoryBean bean = new CronTriggerFactoryBean();        bean.setCronExpression("0/3 * * * * ? 2022");        // 关联JobDetail        bean.setJobDetail(jobDetailFactoryBean().getObject());        return bean;    }        //通过SchedulerFactoryBean来配置Scheduler,来注册Trigger    @Bean    public SchedulerFactoryBean schedulerFactoryBean() {        SchedulerFactoryBean bean = new SchedulerFactoryBean();        // 注册两个Trigger        bean.setTriggers(cronTriggerFactoryBean().getObject());        return bean;    }}

非SpringBoot版

    org.quartz-scheduler    quartz    2.3.2    org.quartz-scheduler    quartz-jobs    2.3.2
public class TaskJob implements Job {    @Override    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {        System.out.println(new Date() + " : 任务「TaskJob 」被执行");    }}
public class MyScheduler {    public static void main(String[] args) throws SchedulerException {        // 1、创建调度器Scheduler        SchedulerFactory schedulerFactory = new StdSchedulerFactory();        Scheduler scheduler = schedulerFactory.getScheduler();        // 2、创建JobDetail实例,并与PrintJob类绑定(Job执行内容)        JobDetail jobDetail = JobBuilder.newJob(PrintJob.class)                .withIdentity("job", "group").build();        // 3、构建Trigger实例,每隔1s执行一次        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger", "triggerGroup")                .startNow()//立即生效                .withSchedule(SimpleScheduleBuilder.simpleSchedule()                        .withIntervalInSeconds(1)//每隔1s执行一次                        .repeatForever()).build();//一直执行        //4、Scheduler绑定Job和Trigger,并执行        scheduler.scheduleJob(jobDetail, trigger);        System.out.println("--------scheduler start ! ------------");        scheduler.start();    }}

六、Xxl-Job分布式任务调度平台

分布式任务调度平台XXL-JOB 

XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用

xxl是xxl-job的开发者大众点评的【许雪里】名称的拼音开头

https://blog.csdn.net/MinggeQingchun/article/details/129883009

可参考

https://www.cnblogs.com/oeong/p/16212448.html

Java 定时任务-最简单的3种实现方法_深海呐的博客-CSDN博客_java定时任务

java定时任务_定时任务3种实现方式_IT枫斗者的博客-CSDN博客_java定时任务

https://www.jb51.net/article/226802.htm

来源地址:https://blog.csdn.net/MinggeQingchun/article/details/126360682

--结束END--

本文标题: Java -- 定时任务实现方式

本文链接: https://www.lsjlt.com/news/401687.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java -- 定时任务实现方式
    在Java开发中,定时任务是一种十分常见的功能. 定时任务是在约定时间内执行的一段程序 如每天凌晨24点备份同步数据,又或者电商平台 30 分钟后自动取消未支付的订单,每隔一个小时拉取一次数据等都需要使用到定时器 批量处理数据:批量统计上个...
    99+
    2023-09-09
    Java Quartz Scheduled Xxl-Job
  • java定时任务实现方式有哪些
    Java中实现定时任务的方式有以下几种:1. 使用Java内置的Timer类,可以通过调度TimerTask来执行任务。Timer提供了简单的任务调度功能,但是不适用于需要精确控制执行时间的任务。2. 使用Java内置的Schedule...
    99+
    2023-08-11
    java
  • Java中定时任务的6种实现方式
    目录1、线程等待实现2、JDK自带Timer实现2.1 核心方法2.2使用示例2.2.1指定延迟执行一次 2.2.2固定间隔执行2.2.3固定速率执行2.3 schedule与sch...
    99+
    2024-04-02
  • java定时任务实现的方式有哪些
    Java中实现定时任务的方式有以下几种:1. Timer类:Java提供了Timer类,可以用来实现简单的定时任务。Timer类允许...
    99+
    2023-08-29
    java
  • Java实现定时任务
    本文实例为大家分享了Java实现定时任务的具体代码,供大家参考,具体内容如下 1 使用java.util.Timer 这种方式的定时任务主要用到两个类,Timer 和 TimerTa...
    99+
    2024-04-02
  • Java中定时任务的实现方式有哪些
    本篇内容主要讲解“Java中定时任务的实现方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中定时任务的实现方式有哪些”吧!1、线程等待实现先从最原始最简单的方式来讲解。可以先创建...
    99+
    2023-06-25
  • Python3-定时任务四种实现方式
    老猫最近做一个小程序开发任务,主要负责后台部分开发;根据项目需求老猫需要实现三个定时任务: 1>定时更新微信token,需要2小时更新一次;2>商品定时上线;3>定时检测后台服务是否存活; 老猫使用Python去实现这...
    99+
    2023-01-31
    四种 方式
  • python 实现定时任务的四种方式
    目录用Python实现定时任务 用Python实现定时任务的四种方法 利用while True: + sleep()实现定时任务 利用threading.Timer定时器实现定时任务...
    99+
    2024-04-02
  • Java 实现定时任务的三种方法
    目录1、 sleep 2、Timer 3、ScheduledExecutorService 总结 是的,不用任何框架,用我们朴素的 Java 编程语言就能实现定时任务。 今天,栈长就...
    99+
    2024-04-02
  • Java实现定时任务的方法详解
    目录前言定时任务是什么定时任务的有哪些是实现方式纯手写单线程循环Timer 和它的小伙伴ScheduledExecutorServiceSpring 提供的定时任务总结前言 学过定时...
    99+
    2022-11-13
    Java实现定时任务 Java定时任务
  • Java如何实现定时任务
    今天小编给大家分享一下Java如何实现定时任务的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、TimerTimer是JAV...
    99+
    2023-07-02
  • SpringBoot实现固定、动态定时任务 | 三种实现方式
    前言: 阅读完本文:🐱‍👓 知晓 SpringBoot 用注解如何实现定时任务明白 SpringBoot 如何实现一个动态定时任务 (与数据库相关联实现)理解 SpringBoot 实现设置时间执行定时任务 ...
    99+
    2023-10-01
    spring boot java mybatis
  • Python定时任务实现方案
    目录1、定时任务2、Python的定时任务2.1 几种常见的方案2.1.1 schedule2.1.2 Jenkins2.1.3 Celery2.2 题外话之持久化2.2.1 ApS...
    99+
    2024-04-02
  • Java Spring怎么实现定时任务
    本篇内容主要讲解“Java Spring怎么实现定时任务”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java Spring怎么实现定时任务”吧!java实现定时任务Jdk自...
    99+
    2023-07-02
  • SpringBoot实现定时任务的三种方式小结
    目录定时任务实现的三种方式使用Timer使用ScheduledExecutorService使用Spring Task1.简单的定时任务2.多线程执行SpringBoot三种方式实现...
    99+
    2024-04-02
  • springboot实现定时任务的四种方式小结
    目录TimerScheduledExecutor注解@ScheduledQuartz因为某些需求,要在特定的时间执行一些任务,比如定时删除服务器存储的数据缓存,定时获取数据以及定时发...
    99+
    2023-01-13
    springboot 定时任务
  • python实现定时任务的8种方式详解
            在日常工作中,常常会用到需要周期性执行的任务,一种方式是采用 Linux 系统自带的 crond 结合命令行实现。另外一种方式是直接使用Python。                 当每隔一段时间就要执行一段程序,或者往复...
    99+
    2023-09-09
    python 定时任务
  • Java项目实现定时任务的三种方法
    目录1 使用java.util.Timer2 使用ScheduledExecutorService3 使用Spring Task总结 1 使用java.util.Timer...
    99+
    2024-04-02
  • Java中怎么实现SpringBoot定时任务
    今天就跟大家聊聊有关Java中怎么实现SpringBoot定时任务,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1. SpringBoot--任务:定时任务项目开发中经常需要执行一些...
    99+
    2023-06-20
  • 怎么在java中实现定时任务
    这篇文章给大家介绍怎么在java中实现定时任务,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一.分类从实现的技术上来分类,目前主要有三种技术(或者说有三种产品):Java自带的java.util.Timer类,这个类允...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作