Quartz定时任务基础

news2024/11/27 16:27:31

springBoot有一个定时执行某个方法的 注解:

@Scheduled

可以满足挺多的需求,但是到了一些场景,就显得比较麻烦,比如:

机器待机五分钟后执行切换待机状态。如果是按照使用@Scheduled注解,就得持久化一个表,里面存放机器信息,转换时间,然后@Scheduled一个方法,每隔一定时间扫描全表,查询需要转换的机器。

还有一种有趣的解决办法:

它会定义一个小顶堆,存放需要执行的任务(主要还是存放的该任务的执行时间),

小顶堆结构;

添加任务只需要在最后一个位置加上,然后上浮操作(和父级对比,父级更大就交换,父级小就不动,一直对比,直到父级小不动),所以会发现,每次都是操作父子级,如何快速找到父级,成了最大问题,于是,存储结构选择了数组:

只需要将子集下标/2=取整,就是父级下标。

存入:

也就是说,每次只需要判断顶部数据是否可以执行即可,比上面那种直接扫描全表性能更快。

执行一个任务后,就会刷新顶堆,拿出最后一个。

取出:

package com.quxiao;

import java.time.LocalTime;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 11:00
 **/
public class t1 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        for (int i = 0; i < 2; i++) {
            TimerTask timerTask = new TimerTest1("" + i);
            timer.schedule(timerTask, new Date(), 2000);
        }
    }

    static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(60));

    static class TimerTest1 extends TimerTask {
        String name;

        public TimerTest1() {

        }

        public TimerTest1(String name) {
            this.name = name;
        }

        @Override
        public void run() {
//如果不使用线程池运行,会导致延迟执行,因为run方法是阻塞的,无法做到异步
            poolExecutor.execute(() -> {
                System.out.println(name + ": " + (LocalTime.now().toString()));
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }
}

Timer是java自带的一个定时任务,定时执行指定的方法,

还有一种就是时间轮,Cron表达式,每个位置指定日期,这里我们使用quartz框架来进行调度器:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

直接使用和springBoot项目运行

首先定义执行什么,怎么执行,以及储存任务

执行什么:

package com.quartz.jop;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalTime;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:00
 **/
public class TestJop implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(LocalTime.now().toString());
    }
}

只有实现了job接口的类才能执行,我们可以在里面执行任何东西。

然后就是怎么执行:

package com.quartz.controller;

import com.quartz.jop.TestJop;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:04
 **/
@RestController
public class t1 {
    @RequestMapping("test1")
    public void test1() {
        //定义需要执行的方法(就是实现类)
        JobDetail jobDetail = JobBuilder.newJob(TestJop.class)
                .withIdentity("job1", "group1")
                .build();
        //如何执行,多少秒一次,什么时候执行,都是在这里定义
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "trigger1")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
                .build();
        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            //储存起来
            scheduler.scheduleJob(jobDetail, trigger);
            //启动    
            scheduler.start();
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        } finally {
        }

    }
}

在定义定时器时,可以将需要的值传入:

package com.quartz.controller;

import com.quartz.jop.TestJop;
import com.quartz.jop.t2;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:04
 **/
@RestController
public class t1 {
    @RequestMapping("test1")
    public void test1() {
        JobDetail jobDetail = JobBuilder.newJob(TestJop.class)
                .withIdentity("job1", "group1")
                .usingJobData("name","你爹")
                .build();

        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "trigger1")
                .usingJobData("name","ty")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(2))
                .build();
        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        } finally {
        }

    }
}

其实就是一个map

取出:

package com.quartz.jop;

import org.quartz.*;

import java.time.LocalTime;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:00
 **/
public class TestJop implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        Trigger trigger = context.getTrigger();
        JobDataMap map = trigger.getJobDataMap();
        System.out.println(map.get("name"));

        System.out.println(LocalTime.now().toString());

        JobDetail jobDetail = context.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        System.out.println(jobDataMap.get("name"));

    }
}

还可以直接把trigger 和jobDetail取出来:

JobDataMap mergedJobDataMap = context.getMergedJobDataMap();

不过很显然,可是不能重复的,map嘛

还有一点就时,调度器默认是并行的:

package com.quartz.jop;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:00
 **/
public class TestJop implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println(1123211);
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(3));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        
//        JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
//        System.out.println(mergedJobDataMap.get("name"));

    }
}

 也就是说,不管上一个任务是否执行完毕,我到了点,本次任务必然执行-。

使用@DisallowConcurrentExecution注解可以变为串行,也就是同步执行

@PersistJobDataAfterExecution可以让jobDetail的jobDataMap一直保存

package com.quartz.jop;

import org.quartz.*;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-18 20:00
 **/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class TestJop implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail jobDetail = context.getJobDetail();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        jobDataMap.put("name", jobDataMap.get("name") + "123");
        System.out.println(jobDataMap.get("name"));
    }
}





cron 模式定时器

前面用的是基础的执行多少次,指定时间执行还是得用cron方式,注意springBoot有自带的cron执行

@Scheduled

注解,但是它只能标识在方法上,也就是说只能某个方法,并不能达到前提条件,例如:

设备闲置5分钟,改为待机状态,只要在五分钟内运行,我们就把这个定时任务删掉就好。

package com.quartz.util;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @program: springBoot
 * @author: quxiao
 * @create: 2023-11-26 10:56
 **/
public class DateUtil {
    /**
     * @param date 指定日期
     * @return 返回指定日期的cron表达式
     */
    public synchronized static String getCron(Date date) {
        String dateFormat = "ss mm HH dd MM ? yyyy";

        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }

    /**
     * @param date 指定日期
     * @param unit 时间单位
     * @param number 增加多少
     * 增加指定时间
     */
    public synchronized static Date addTime(Date date, TemporalUnit unit,long number){
        LocalDateTime date1 = date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
        LocalDateTime nextWeek = date1.plus(number, unit);
        return Date.from(nextWeek.atZone(ZoneId.systemDefault()).toInstant());
    }
}

 先给一个时间转换的工具类

使用cron方式只需要将触发器改了就行: (接上序的基础springboot代码)

  @Autowired
    Scheduler scheduler;

    @PostMapping("test1")
    public void test1() {
        JobDetail jobDetail = JobBuilder.newJob(TestJop.class)
                .withIdentity("job1", "group1")
                .build();

        Date date = DateUtil.addTime(new Date(), ChronoUnit.SECONDS, 5);
        //cron表达式
        String cron = DateUtil.getCron(date);
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "trigger1")
                .withSchedule(CronScheduleBuilder.cronSchedule(cron))//达到指定时间执行
                .build();
        try {
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

 

 我们之所以使用这个框架,就是因为他可以支持删除、暂停、定时任务,所以就能够把定时任务存起来持久化管理。

删除、暂停、恢复定时任务

 @PostMapping("test2")
    public void t2() {
        try {
            JobKey key = new JobKey("job1","group1");
            scheduler.deleteJob(key);
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

在这里我们前面定义的group分组就起到作用了,通过指定定时任务名job金额分组名group。

所以剩余两个操作也是一样:

  @PostMapping("test3")
    public void t3() {
        try {
            JobKey key = new JobKey("job1", "group1");
            scheduler.pauseJob(key);
            for (int i = 0; i < 10; i++) {
                System.out.println("暂停中");
                TimeUnit.SECONDS.sleep(1);
            }
            scheduler.resumeJob(key);
        } catch (Exception e) {
        }
    }

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

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

相关文章

【5G PHY】5G SS/PBCH块介绍(四)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

利用ngrok实现内网穿透(全网最详细教程)

准备工具&#xff1a; 1、phpstudy 用于在本地搭建网站 2、ngrok 用于将自己的本地端口暴露到公网上&#xff0c;从而实现内网穿透 文章开始前给大家分享一个学习人工智能的网站&#xff0c;通俗易懂&#xff0c;风趣幽默 人工智能https://www.captainbed.cn/myon/ ~~~~~…

C#文件基本操作(判断文件是否存在、创建文件、复制或移动文件、删除文件以及获取文件基本信息)

目录 一、判断文件是否存在 1.File类的Exists()方法 2.FileInfo类的Exists属性 二、创建文件 1.File类的Create()方法 2.FileInfo类的Create()方法 三、复制或移动文件 1.File类的Copy()方法 2.File类的Move()方法 3.FileInfo类的CopyTo()方法 四、删除文件 1.File…

大数据数据仓库,Sqoop--学习笔记

数据仓库介绍 1. 数据仓库概念 数据仓库概念创始人在《建立数据仓库》一书中对数据仓库的定义是&#xff1a;数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的&#xff08;Subject Oriented&#xff09;、数据集成的&#xff08;Integrated&#xff09;、相对…

【AUTOSAR】【通信栈】ComXf

AUTOSAR专栏——总目录_嵌入式知行合一的博客-CSDN博客文章浏览阅读292次。本文主要汇总该专栏文章,以方便各位读者阅读。https://xianfan.blog.csdn.net/article/details/132072415 目录 一、概述 二、限制说明

NX二次开发UF_CURVE_ask_curve_turn_angle 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_turn_angle Defined in: uf_curve.h int UF_CURVE_ask_curve_turn_angle(tag_t curve, double orientation [ 3 ] , double * angle ) overview 概述 Returns …

Python大数据考题

Python大数据考题&#xff1a; 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql要…

Typescript基础面试题 | 01.精选 ts 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Python 潮流周刊#28:两种线程池、四种优化程序的方法

△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯。 &#x1f43…

Typescript基础面试题 | 03.精选 ts 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

C# WPF上位机开发(开篇)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前很少用到c#语言&#xff0c;大部分时间都用c/c&#xff0c;主要是它可以兼顾上位机qt开发以及嵌入式开发。所以&#xff0c;用c/c是比较合理的…

新手如何买卖股票,股票投资基础入门

一、教程描述 本套股票教程&#xff0c;大小3.89G&#xff0c;共有13个文件。 二、教程目录 第01课【极速入门】股市全景.mp4 第02课【极速入门】入门实操.mp4 第03课【价值分析】白马选股.mp4 第04课【价值分析】行业选股.mp4 第05课【价值分析】量化选股.mp4 第06课【…

vue3+ts 依赖注入 provide inject

父级&#xff1a; <template><div><h1>App.vue (爷爷级别)</h1><label><input type"radio" v-model"colorVal" value"red" name"color" />红色</label><label><input type"r…

leetCode 1080.根到叶路径上的不足节点 + 递归 + 图解

给你二叉树的根节点 root 和一个整数 limit &#xff0c;请你同时删除树中所有 不足节点 &#xff0c;并返回最终二叉树的根节点。假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit&#xff0c;则该节点被称之为 不足节点 &#xff0c;需要被删除…

养生馆服务预约会员管理系统小程序效果如何

中医养生馆的全国数量逐渐增加&#xff0c;各种疾病困扰下&#xff0c;有些病往往通过养生馆即可治好&#xff0c;比如常见的针灸、按摩、药理滋补、切脉等&#xff0c;都有很高的市场需求度&#xff0c;而随着众多商家入局赛道及消费升级&#xff0c;传统中医养生馆经营痛点也…

【C++】哈希(位图、布隆过滤器)

一、哈希的应用&#xff08;位图和布隆过滤器&#xff09; 1、位图&#xff08;bitset&#xff09; &#xff08;1&#xff09;位图概念 【题目】 给 40亿 个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这 40亿 个数中。…

电源的纹波

电源纹波的产生 我们常见的电源有线性电源和开关电源&#xff0c;它们输出的直流电压是由交流电压经整流、滤波、稳压后得到的。由于滤波不干净&#xff0c;直流电平之上就会附着包含周期性与随机性成分的杂波信号&#xff0c;这就产生了纹波。 在额定输出电压、电流的情况下…

Shell脚本:Linux Shell脚本学习指南(第三部分Shell高级)二

七、Shell Here String&#xff08;内嵌字符串&#xff0c;嵌入式字符串&#xff09; Here String 是《六、Shell Here Document&#xff08;内嵌文档/立即文档&#xff09;》的一个变种&#xff0c;它的用法如下&#xff1a; command <<< string command 是 Shell 命…

指定训练使用的GPU个数,没有指定定gpu id,训练在其中两个gpu上执行,但是线程id分布在所有4个gpu上,为什么?如何解决?

目录 问题背景 1 线程id分布在所有gpu&#xff08;包括未启用的gpu&#xff09;上原因&#xff1a; 2 在解决这个问题时&#xff0c;可以采取以下步骤&#xff1a; 3 修正深度学习框架默认使用所有可见 GPU 的问题 1 TensorFlow&#xff1a; 2 PyTorch&#xff1a; 3 K…

【【Linux编程介绍之关键配置和常用用法】】

Linux编程介绍之关键配置和常用用法 Hello World ! 我们所说的编写代码包括两部分&#xff1a;代码编写和编译&#xff0c;在Windows下可以使用Visual Studio来完成这两部&#xff0c;可以在 Visual Studio 下编写代码然后直接点击编译就可以了。但是在 Linux 下这两部分是分开…