Java常用三类定时器快速入手指南

news2024/12/25 13:45:58

文章目录

  • Java常用三类定时器快速入手指南
    • 一、序言
    • 二,Timer相关
      • 1、概念
      • 2、Timer类
      • 3、TimerTask类
      • 4、ScheduleExecutorService接口
    • 三,Scheduled相关
      • 1、配置
        • 1.1 SpringMVC配置
        • 1.2 SpringBoot配置
          • (1)单线程
          • (2)多线程
      • 2、使用方法
        • 2.1 cron表达式
        • 2.2 其他参数
    • 四,Quartz相关
      • 1、概念
      • 2、使用方法
        • 2.1 导入依赖
        • 2.2 实现功能
        • 2.3 示例
      • 3、进阶使用
        • 3.1 存储数据
          • (1)JobDataMap及jobDetail
          • (2)私有属性
        • 3.2 Job并发与持久化
          • (1)并发
          • (2)持久化
        • 3.3 Trigger
          • (1)通用属性
          • (2)SimpleTrigger介绍
          • (3)CronTrigger介绍
      • 4、集群
        • 4.1 步骤一
        • 4.2 步骤二
        • 4.3 步骤三
      • 5、实例代码
        • 5.1 实体entity
        • 5.2 业务service


Java常用三类定时器快速入手指南

一、序言

小豪近来工作有遇到定时任务相关的需求,通常情况下,使用Spring框架自带的Scheduled注解就能满足大部分简单任务的需求,最近遇到的定时任务需求稍显复杂,Scheduled已经有些力不从心了,遂考虑集成第三方定时任务框架Quartz组件,完成业务需求。

本文小豪着重以代码案例的形式呈现,旨在帮助初学者快速入手Java定时器,掌握Java中常用三类定时任务的基本概念及用法。本文大纲如下:

在这里插入图片描述

二,Timer相关

1、概念

Timerjava.util.Timer工具类,java.util包主要使用TimerTimerTask这两个类:

  • Timer直接从Object继承,相当计时器,能够用它来指定某个时间来执行一项任务,或者每隔一定时间间隔反复执行同一个任务。创建一个Timer后,就会生成一个线程在背后运行,来控制任务的执行。
  • TimerTask就是用来实现某项任务的类,它实现了Runnable接口,因此相当于一个线程。

2、Timer类

(1)构造方法

// 创建一个新计时器
Timer()

// 创建新计时器,可指定其相关的线程作为守护线程运行
Timer(boolean isDaemon)

// 创建新计时器,设置指定名称
Timer(String name)

// 创建新计时器,设置指定名称及是否守护线程
Timer(String name, boolean isDaemon)

(2)普通方法

// 安排在指定延迟后执行指定的任务
/* 参数:
 task - 所要安排的任务
 delay - 执行任务前的延迟时间,单位是毫秒
*/
void schedule(TimerTask task,long delay)
// 例:程序启动后3秒之后执行
Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器执行。。。");
            }
        }, 3000);

// 安排在指定的时间执行指定的任务。如果此时间已过去,则安排立即执行该任务(参数二:new Date() 代表立刻执行)
void schedule(TimerTask task,Date time)

// 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行
/* 参数:
 task - 所要安排的任务
 delay - 执行任务前的延迟时间,单位是毫秒
 period - 执行各后续任务之间的时间间隔,单位是毫秒
*/
void schedule(TimerTask task,long delay,long period)

// 安排指定的任务在指定的时间开始进行重复的固定延迟执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行
void schedule(TimerTask task,Date firstTime,long period)

// 安排指定的任务在指定的延迟后开始进行重复的固定速率执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行
void scheduleAtFixedRate(TimerTask task,long delay,long period)

// 安排指定的任务在指定的时间开始进行重复的固定速率执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行
void scheduleAtFixedRate(TimerTask task,Date firstTime,long period)

// 终止此计时器,丢弃所有当前已安排的任务
void cancel()

// 从此计时器的任务队列中移除所有已取消的任务
int purge()

3、TimerTask类

(1)构造方法

// 创建新的计时器任务
TimerTask()

(2)普通方法

// 取消此计时器任务
boolean cancel()

// 返回此任务最近实际执行的安排执行时间
long scheduledExecutionTime()

// 此计时器任务要执行的操作(接口Runnable中的run)
void run()

4、ScheduleExecutorService接口

(1)概念

ScheduledExecutorService线程池实现循环或延迟任务。

ScheduledExecutorServiceTimer 的区别:

  1. Timer的内部只有一个线程,如果有多个任务的话就会顺序执行,会导致延迟时间和循环时间出现问题
  2. ScheduledExecutorService是线程池,不会出现此问题,在对延迟任务和循环任务要求严格的时候,可使用ScheduledExecutorService

(2)使用方法

// 1.创建ScheduledExecutorService的实例(参数指定最大线程数量)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
// 2.调用方法
scheduledExecutorService.scheduleAtFixedRate(xxx...);

(3)普通方法

// 在给定的延迟之后创建并执行启用的一次性操作。
/*
 command - 命令要执行的任务
 delay - 从现在开始延迟执行的时间
 unit - 延迟参数的时间单位
*/
ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit);

ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

// 在给定的初始延迟之后创建并执行启用的周期性操作
/*
 command - 命令要执行的任务
 initialDelay - 延迟第一次执行的时间
 period - 连续执行之间的时间间隔
 unit - initialDelay和period参数的时间单位
*/
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

// 在给定的初始延迟之后创建并执行先启用的周期性操作
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

三,Scheduled相关

1、配置

1.1 SpringMVC配置
<!--1.在xml里加入task的命名空间-->
xmlns:task="http://www.springframework.org/schema/task" 
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
 
<!--2.启用注解驱动的定时任务-->
<task:annotation-driven scheduler="myScheduler"/>

<!--3.配置定时任务的线程池-->
<task:scheduler id="myScheduler" pool-size="5"/>
1.2 SpringBoot配置
(1)单线程

在启动类添加@EnableScheduling注解即可

(2)多线程
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
 
	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setScheduler(taskExecutor());
	}
 
	@Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(5 ,new ThreadFactory() {
        	private final AtomicLong counter = new AtomicLong();
			
			@Override
			public Thread newThread(Runnable r) {
				Thread thread = new Thread(r);
				thread.setName("scheduler-" + counter.incrementAndGet());
				return thread;
			}	
		});
    }	
}

2、使用方法

@Scheduled注解使用的方法必须是在@Component注解下的类

2.1 cron表达式
<--在方法上使用@Scheduled,并配置对应参数即可-->
使用cron表达式的方法,在项目首次启动后不会直接运行,而是等到执行周期才会执行( 0/2 * * * * ? 表示每2秒执行任务)
    
<--在线cron生成地址-->
https://www.bejson.com/othertools/cron/
2.2 其他参数

在这里插入图片描述

// 使用fixedRate/fixedDelay方式的定时器方法,在项目启动成功后会马上开始执行一次,再按照时间周期执行
// 例:@Scheduled(fixedDelay = 1000 * 5)

fixedRate/fixedDelay区别:
fixedRate是配置上一次任务执行开始到下一次执行开始的间隔时间,不会等待上一次任务执行完成就会调度下一次任务,将其放入等待队列中
fixedDelay是配置的上一次任务执行结束到下一次执行开始的间隔时间,也就是说会等待上一次任务执行结束后,延迟间隔时间,再执行下一次任务

四,Quartz相关

1、概念

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。相较Timer, Quartz强大许多,具备分布式、持久性作业、调度任务管理能力。

Quartz的基本组成部分:

  • 调度器:Scheduler
  • 任务:JobDetail
  • 触发器:Trigger,包括SimpleTriggerCronTrigger

2、使用方法

2.1 导入依赖
<!-- springboot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
或者:
<!-- springmvc -->
<dependency> 
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
</dependency>
2.2 实现功能

(1)定义实现定时功能的接口,称之为Task(或Job

在这里插入图片描述

(2)实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等

在这里插入图片描述

(3)使用Schedule,将JobTrigger两者结合起来,来负责功能的实现

在这里插入图片描述

2.3 示例

(1)新建一个能够打印任意内容的Job

public class PrintWordsJob implements Job {

    // 此类实现Job接口,复写execute()方法
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("定时器执行啦。。。"+new Date());
    }
}

(2)创建Schedule,执行任务

public class MyScheduler {

    public static void main(String[] args) throws SchedulerException {
        // 1.创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2.创建JobDetail实例,并与PrintWordsJob自定义类绑定(Job执行内容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                .withIdentity("job1", "group1").build();
        // 3.构建Trigger实例,每隔3s执行一次
        // 使用SimpleScheduleBuilder或者CronScheduleBuilder
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .startNow()//立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(3)//每隔3s执行一次
                        .repeatForever()).build();//一直执行
        // 4.注册任务和定时器
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start! ------------");
        // 5.启动调度器
        scheduler.start();
    }

}

3、进阶使用

3.1 存储数据
(1)JobDataMap及jobDetail
  • JobDataMapJava Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法,用于保存任务实例的状态信息
  • jobDetail:包含job的各种属性设置,以及用于存储job实例状态信息的JobDataMap

a.构建方式

//方式一:直接在构建JobDetail时,通过JobBuilder的usingJobData方法将数据放入JobDataMap中

// 构建JobDetail实例
JobDetail job = JobBuilder.newJob(HelloJob.class)
	.withIdentity("helloJob", "hello")//给job命名并分组
	.usingJobData("jobdd", "hello job")//通过JobBuilder的usingJobData方法给JobDataMap中存数据
	.build();
//方式二:构建JobDataMap,然后存入JobDetail中。或者从JobDetail中获取往里面put数据

// 构建JobDetail实例
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobcc", "哈哈");
JobDetail job = JobBuilder.newJob(HelloJob.class)
	.withIdentity("helloJob", "hello")//给job命名并分组
	.usingJobData("jobdd", "hello job")//通过JobBuilder的usingJobData方法向JobDataMap中存数据
	.usingJobData(jobDataMap)
	.build();

b.取数据方法

// 在job的执行过程中,可以从JobDataMap中取出数据

// JobDetail的key由name和group组成
JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
// Trigger的key由name和group组成
TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey();
String bob = jobExecutionContext.getJobDetail().getJobDataMap().getString("jobdd");
String bob = jobExecutionContext.getJobDetail().getJobDataMap().getString("jobcc");
(2)私有属性

自定义私有属性,job被实例化的时会自动调用其set方法

public class HelloJob implements Job {

    //定义私有属性,会自动封装给job
	private String jobcc;
	private String jobdd;
	private String jobaa;

	public void setJobcc(String jobcc) {
		this.jobcc = jobcc;
	}

	public void setJobdd(String jobdd) {
		this.jobdd = jobdd;
	}

	public void setJobaa(String jobaa) {
		this.jobaa = jobaa;
	}

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		System.err.println(context.getJobDetail().getKey());// JobDetail的key由name和group组成
		System.err.println(context.getTrigger().getKey());// Trigger的key由name和group组成
		System.err.println(jobcc);
		System.err.println(jobdd);
		System.err.println(jobaa);
		System.err.println("hello,quartz");
	}

}
3.2 Job并发与持久化

仅在job类上加入部分注解,即可实现Job的并发与持久化。

(1)并发
@DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。
(2)持久化
@PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。

若使用@PersistJobDataAfterExecution注解,同时建议使用@DisallowConcurrentExecution注解
3.3 Trigger

TriggerQuartz框架中的核心接口,其最重要的两个实现类是CronTriggerImplSimpleTriggerImpl

(1)通用属性
  • JobKey:表示与该Trigger绑定的Job实例的标识,触发器被触发时,该指定的Job实例会被执行

  • StartTime:表示触发器的时间表首次被触发的时间,Java.util.Date类型

  • EndTime:指定触发器不再被触发的时间,Java.util.Date类型

(2)SimpleTrigger介绍

在一个指定时间段内执行一次作业任务或是在指定的时间间隔内多次执行作业任务

// 实例:距离当前时间4秒钟之后首次执行任务,6秒后停止,每隔2秒执行,执行3次
// 创建一个SimpleTrigger实例,定义该Job4秒后执行,并且每隔两秒钟重复执行一次,6秒后停止
SimpleTrigger trigger=(SimpleTrigger)TriggerBuilder
	.newTrigger()
	.withIdentity("myTrigger", "group1")//定义name和group
	.startAt(date)//设置开始时间
	.endAt(endDate)//设置结束时间 
	.withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger 
	.withIntervalInSeconds(2)//每隔两秒执行一次 
	.withRepeatCount(3))//重复执行三次 
	.build();
(3)CronTrigger介绍

基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,SimpleTrigger能够实现的,CronTrigger都能够实现,基本普遍都用CronTrigger

// 创建一个CronTrigger实例
CronTrigger trigger=(CronTrigger)TriggerBuilder.newTrigger()
	.withIdentity("myTrigger", "group1")//定义name/group
	.withSchedule(CronScheduleBuilder.cronSchedule("0 5 21 * * ? *"))//填写cron表达式
	.build();

4、集群

4.1 步骤一

(1)下载quartz表

http://www.quartz-scheduler.org/downloads/

(2)表功能说明

在这里插入图片描述

(3)导入配置文件(quartz.properties)

# 配置JobStore,JobDataMaps是否都为String类型,默认false
org.quartz.jobStore.useProperties=false

# 表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_

# 是否加入集群
org.quartz.jobStore.isClustered = true

# 调度实例失效的检查时间间隔 ms
org.quartz.jobStore.clusterCheckinInterval = 5000

# 当设置为“true”时,此属性告诉Quartz 在非托管JDBC连接上调用setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)。
org.quartz.jobStore.txIsolationLevelReadCommitted = true

# 数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

# 数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

# Scheduler 调度器属性配置
# 调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName = ClusterQuartz
# ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceId= AUTO

# 配置ThreadPool
# 线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

# 指定线程数,一般设置为1-100直接的整数,根据系统资源配置
org.quartz.threadPool.threadCount = 10

# 设置线程的优先级(可以是Thread.MIN_PRIORITY(即1)和Thread.MAX_PRIORITY(这是10)之间的任何int 。默认值为Thread.NORM_PRIORITY(5)。)
org.quartz.threadPool.threadPriority = 5

(4)配置数据源

spring.datasource.url=jdbc:mysql://localhost:3306/xiaohao?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.max-active=1000
spring.datasource.max-idle=20
spring.datasource.min-idle=5
spring.datasource.initial-size=10
4.2 步骤二

(1)QuartzConfigure配置类

@Configuration
public class SchedulerConfig {

    /**
     * 配置文件路径
     */
    private static final String QUARTZ_CONFIG = "/quartz.properties";

    /**
     * 注入数据源
     */
    @Autowired
    private DataSource dataSource;

    /**
     * 通过SchedulerFactoryBean获取Scheduler的实例
     */
    @Bean
    public Scheduler scheduler() throws IOException {
        return schedulerFactoryBean().getScheduler();
    }

    /**
     * 配置SchedulerFactory
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("cluster_scheduler");
        factory.setDataSource(dataSource);
        //将spring上下文放入quartz中, 用于在job执行时获取注入bean
        factory.setApplicationContextSchedulerContextKey("application");
        factory.setQuartzProperties(quartzProperties());
        factory.setTaskExecutor(schedulerThreadPool());
        //延时3秒启动
        factory.setStartupDelay(3);
        return factory;

    }

    /**
     * 从quartz.properties文件中读取Quartz配置属性
     */
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_CONFIG));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    /**
     * 自定义线程池
     */
    @Bean
    public Executor schedulerThreadPool(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
        executor.setQueueCapacity(Runtime.getRuntime().availableProcessors());
        return executor;
    }
}

(2)QuartzJob定时任务类

// QuartzJobBean抽象类默认实现Job接口,并对工厂进行了一些自动化配置
// 及时更新JobDetail中JobDataMap的数据
@PersistJobDataAfterExecution
// 标记于实现Job的类上,代表不允许并发执行
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {

    /**
     * Quartz Job真正的执行逻辑
     */
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            System.out.println(context.getScheduler().getSchedulerInstanceId());
            System.out.println("定时器执行啦。。。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
4.3 步骤三

(1)定义任务

@Controller
public class MyTask {

    @Autowired
    private Scheduler scheduler;

    @RequestMapping("/setScheduler")
    @ResponseBody
    public String setScheduler() throws SchedulerException {

        // 1.创建JobDetail实例,并与QuartzJob自定义类绑定(Job执行内容)
        JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                .withIdentity("job1", "group1")
                .build();
        // 2.构建Trigger实例,每隔3s执行一次
        // 使用SimpleScheduleBuilder或者CronScheduleBuilder
        Trigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity("trigger1", "triggerGroup1")
                .startNow()//立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(3)//每隔3s执行一次
                        .repeatForever()).build();//一直执行

        // 3.注册任务和定时器,交给scheduler调度
        scheduler.scheduleJob(jobDetail, trigger);
        
        return "hello,quartz";
    }
}

5、实例代码

5.1 实体entity
@Data
public class JobAndTrigger {
    /**
     * 任务名称(*)
     */
    private String jobName;
    /**
     * 任务所在组(*)
     */
    private String jobGroup;
    /**
     * 任务类名(*)
     * 例:"com.example.demo.task.QuartzJob"
     */
    private String jobClassName;
    /**
     * 触发器名称
     */
    private String triggerName;
    /**
     * 触发器所在组
     */
    private String triggerGroup;
    /**
     * cron表达式(*)
     */
    private String cronExpression;
    /**
     * 时区
     */
    private String timeZoneId;
    /**
     * 任务描述(*)
     */
    private String description;
    /**
     * 状态
     */
    private String triggerState;
}
5.2 业务service
@Service
@Slf4j
public class JobService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 添加一个定时任务
     *
     * @param jobAndTrigger
     */
    public void addCronJob(JobAndTrigger jobAndTrigger) throws SchedulerException {
        try {
            JobKey jobKey = JobKey.jobKey(jobAndTrigger.getJobName(), jobAndTrigger.getJobGroup());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail != null) {
                log.error("job:" + jobAndTrigger.getJobName() + " 已存在");
            } else {
                // 创建JobDetail实例,并与QuartzJob自定义类绑定(Job执行内容)
                jobDetail = JobBuilder.newJob((Class<Job>) Class.forName(jobAndTrigger.getJobClassName()))
                        .withIdentity(jobAndTrigger.getJobName(), jobAndTrigger.getJobGroup())
                        .withDescription(jobAndTrigger.getDescription())
                        .build();
                // 用JopDataMap来传递数据
                jobDetail.getJobDataMap().put("taskData", "定时任务传递数据");

                // 表达式调度构建器,首先校验表达式格式
                if (!CronExpression.isValidExpression(jobAndTrigger.getCronExpression())) {
                    log.error("cron表达式规则不正确");
                }
                CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(jobAndTrigger.getCronExpression());

                // 根据cronExpression表达式构建新的trigger
                Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobAndTrigger.getJobName() + "_trigger", jobAndTrigger.getJobGroup() + "_trigger")
                        .withSchedule(cron)
                        .withDescription(jobAndTrigger.getDescription())
                        .build();
                // 注册任务和定时器,交给scheduler调度
                scheduler.scheduleJob(jobDetail, trigger);
            }
        } catch (Exception e) {
            log.error("任务创建失败:", e);
        }
    }

    /**
     * 暂停任务
     *
     * @param jobName
     * @param jobGroup
     */
    public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "_trigger", jobGroup + "_trigger");
            scheduler.pauseTrigger(triggerKey);
        } catch (SchedulerException e) {
            log.error("定时任务暂停失败:", e);
        }
    }

    /**
     * 恢复任务
     *
     * @param jobName
     * @param jobGroup
     */
    public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "_trigger", jobGroup + "_trigger");
            scheduler.resumeTrigger(triggerKey);
        } catch (SchedulerException e) {
            log.error("定时任务恢复失败:", e);
        }
    }

    /**
     * 修改任务表达式并重新调度
     *
     * @param jobAndTrigger
     */
    public void rescheduleJob(JobAndTrigger jobAndTrigger) throws SchedulerException {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobAndTrigger.getJobName() + "_trigger", jobAndTrigger.getJobGroup() + "_trigger");
            // 表达式调度构建器
            CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(jobAndTrigger.getCronExpression());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            String oldCron = trigger.getCronExpression();
            if (oldCron.isEmpty() || !oldCron.equals(jobAndTrigger.getCronExpression())) {
                // 表达式调度构建器,首先校验表达式格式
                if (!CronExpression.isValidExpression(jobAndTrigger.getCronExpression())) {
                    log.error("cron表达式规则不正确");
                }
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cron).build();
                // 按新的trigger重新设置job执行
                scheduler.rescheduleJob(triggerKey, trigger);
            }
        } catch (SchedulerException e) {
            log.error("定时任务修改失败:", e);
        }
    }

    /**
     * 删除job
     *
     * @param jobName
     * @param jobGroup
     */
    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.error("定时任务删除失败:", e);
        }
    }

}

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

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

相关文章

python 如何引用变量

在字符串中引入变量有三种方法&#xff1a; 1、 连字符 name zhangsan print(my name is name) 结果为 my name is zhangsan 2、% 字符 name zhangsan age 25 price 4500.225 print(my name is %s%(name)) print(i am %d%(age) years old) print(my price is %f%(pric…

【数字图像处理】小白也能懂,最浅显方式手撕直方图均衡化(附python实现)

文章目录 1 概念2 原理2.1 数学原理 3 python代码实现4 测试效果5 结论 1 概念 直方图均衡化&#xff0c;同伽马变换一样&#xff0c;也是增强图像对比度的一种工具。区别在于&#xff0c;直方图均衡化是一种自适应的工具&#xff0c;即自动工具。也就是说&#xff0c;我们只需…

使用RestTemplate调用EMQX API查询MQTT客户端列表信息

项目中集成mqtt客户端查询功能&#xff0c;使用到了EMQX api-v5&#xff0c;具体步骤&#xff1a; 一、准备工作 首先在EMQX dashboard中添加API 密钥 填写密钥名称&#xff0c;点击确定&#xff0c;会生成API Key和Secret Key&#xff0c;保存起来备用。 二、配置文件 在…

SUP-NeRF-ECCV2024数据集: 单目3D对象重建的新突破

2024-09-25&#xff0c;由Bosch Research North America和Michigan State University联合发布的SUP-NeRF&#xff0c;是一个基于单目图像进行3D对象重建的新型方法。一个无缝集成姿态估计和物体重建的统一网格。 ECCV&#xff1a;欧洲计算机视觉会议的缩写&#xff0c;它是计算…

如何使用ssm实现科技银行业务管理系统+vue

TOC ssm743科技银行业务管理系统vue 第一章 绪论 1.1 研究背景 在现在社会&#xff0c;对于信息处理方面&#xff0c;是有很高的要求的&#xff0c;因为信息的产生是无时无刻的&#xff0c;并且信息产生的数量是呈几何形式的增加&#xff0c;而增加的信息如何存储以及短时间…

移除元素

移除元素 题目链接&#xff1a;移除元素 示例 1&#xff1a; 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2,_,_] 解释&#xff1a;你的函数函数应该返回 k 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要&…

URL从输入到⻚面显示的过程(详细版)

URL从输入到⻚面显示的过程&#xff08;详细版&#xff09; 浏览器中输入网址 DNS 解析域名得到 IP 地址 DNS 解析首先会从你的浏览器的缓存中去寻找是否有这个网址对应的 IP 地址&#xff0c;如果没有就向OS系统的 DNS 缓存中寻找&#xff0c;如果没有就是路由器的 DNS 缓存&…

C++之 友元重载 以及最常用的几种友元函数

在之前的友元中就曾经讲过&#xff0c;我们为了去访问修改私有成员中的数据时&#xff0c;只能通过公有的办法去进行访问操作&#xff0c;非常的局限。所以C引用了友元函数&#xff0c;只要加上friend关键字&#xff0c;C的这个类&#xff0c;会自动把这个函数的权限拉到类内&a…

无水印短视频素材下载网站有哪些?十个高清无水印视频素材网站分享

你知道怎么下载无水印视频素材吗&#xff1f;今天小编就给大家推荐十个高清无水印视频素材下载的网站&#xff0c;如果你也是苦于下载高清无水印的短视频素材&#xff0c;赶紧来看看吧&#xff5e; 1. 稻虎网 首推的是稻虎网。这个网站简直就是短视频创作者的宝库。无论你需要…

编程魔法:基于LLM的AI function开发,如何实现高效数据生成?

基于大语言模型&#xff08;LLM&#xff09;的AI function开发&#xff0c;简直就是现代编程界的“魔法棒”&#xff01; 你好&#xff0c;我是三桥君 最近三桥君有个任务&#xff0c;需要造一些测试数据&#xff0c;比如姓名、手机号、银行卡号、邮箱啥的&#xff0c;用来做测…

每日OJ题_牛客_添加逗号_模拟_C++_Java

目录 牛客_添加逗号_模拟 题目解析 C代码1 C代码2 Java代码 牛客_添加逗号_模拟 添加逗号_牛客题霸_牛客网 题目解析 读取输入&#xff1a;读取一行字符串。分割字符串&#xff1a;使用空格将字符串分割成单词数组。拼接字符串&#xff1a;将单词数组中的每个单词用逗号…

群晖安装Gitea(代码托管工具)

一、Gitea介绍 Gitea 是一款开源的轻量级代码托管平台,可以为团队和开发者提供了一个易于部署、运行快速、使用体验良好的自建 Git 服务。相比于其它自部署代码托管平台,Gitea 的设计更加轻量,对系统资源的占用相对较少,能够在较低配置的服务器上流畅运行。相比于其他代码…

嘉楠科技AI芯片K230-初探

勘智K230 介绍入门购买开发板 安装开机开发学习点亮第1个LED点亮屏幕预览摄像头代码离线运行 在线训练平台 参考 介绍 K230芯片是嘉楠科技 Kendryte系列AIoT芯片中的最新一代SoC产品。该芯片采用全新的多异构单元加速计算架构&#xff0c;集成了2个RISC-V高能效计算核心&#x…

Spring系列 BeanPostProcessor

文章目录 BeanPostProcessor注册时机执行时机 InstantiationAwareBeanPostProcessorSmartInstantiationAwareBeanPostProcessor 本文源码基于spring-beans-5.3.31 参考&#xff1a;https://docs.spring.io/spring-framework/reference/core/beans/factory-extension.html#beans…

【ASE】第四课_高亮显示效果(手动切换)

今天我们一起来学习ASE插件&#xff0c;希望各位点个关注&#xff0c;一起跟随我的步伐 今天我们来学习高亮的效果。 思路&#xff1a; 1.添加纹理贴图和法线贴图&#xff0c;环境光遮挡贴图 2.添加高亮的参数&#xff0c;并设置 3.手搓一个边缘光,通过高亮参数调节 4.将模…

微信小程序——音乐播放器

目的 掌握swiper组件、scroll-view组件的使用掌握image组件的使用掌握音频API的使用掌握slider组件的使用 内容 了音乐小程序项目的完整开发流程&#xff0c;其开发步骤包括页面结构的分析、样式的设计、组件的运用等。通过本章的学习&#xff0c;读者能够掌握小程序的基本交…

聚星文社——绘唐科技有什么区别!

聚星文社和绘唐科技是两个不同的公司&#xff0c;有一些区别。下面是它们的一些区别&#xff1a; 绘唐科技——聚星文社https://iimenvrieak.feishu.cn/docx/ZhRNdEWT6oGdCwxdhOPcdds7nof 行业领域&#xff1a;聚星文社主要从事文化娱乐行业&#xff0c;包括出版、影视制作等&…

点餐小程序实战教程14点餐功能

目录 1 功能分析2 初始化菜品的数量3 加入购物车4 显示购物车5 最终的效果总结 上一篇我们讲解了如果通过扫码实现餐桌信息显示&#xff0c;本篇我们介绍一下点餐的功能。 1 功能分析 点餐的话一般我们是在菜品点击号或者-号来加入购物车&#xff0c;加入购物车之后还可以修改…

APP 安全测试项总结

一、安装包测试 1.1、关于反编译 目的是为了保护公司的知识产权和安全方面的考虑等&#xff0c;一些程序开发人员会在源码中硬编码一些敏感信息&#xff0c;如密码。而且若程序内部一些设计欠佳的逻辑&#xff0c;也可能隐含漏洞&#xff0c;一旦源码泄漏&#xff0c;安全隐患…

Temporal Dynamic Quantization for Diffusion Models阅读

文章目录 AbstractIntroductionBackgrounds and Related Works2.1 扩散模型2.2 量化2.3 量化感知训练和训练后量化 TemporalDynamic Quantization3.1 量化方法3.2 扩散模型量化的挑战3.3 TDQ模块的实现3.4 工程细节时间步的频率编码TDQ模块的初始化 Experimental SetupResults5…