【定时任务】调度器Quartz和线程池的关系浅解

1.在最近的实际开发项目中,开发了一段定时任务处理逻辑,然后不太明白里面的原理,就查询资料和个人理解。见下代码,主要有两个类,第一个类是实现了Job的quartz定时任务类;第二个就是创建任务调度并且启动所有定时执行任务类。

(1)主要jar包,引入quartz-1.6.0.jar;无配置文件quartz.properties。

注意:当我们在我们的项目中添加了quartz.properties的时候,我们需要把那些我们不需要修改的配置也加上,并不能只有我们修改的配置。因为通过查看源代码我们可以知道,调度器在初始化的时候,首先判断我们是否有quartz.properties,如果有的话,就是用我们系统中的配置文件,否则就从jar包里加载quartz.properties进行默认的初始化。

配置文件如下:

我们需要copy一份这个属性文件,并在这个文件的基础上修改线程池的配置:

#可以是任何正整数,虽然你应该意识到只有1到100之间的数字是非常实用的。这是可用于并发执行作业的线程数。如果你只有几个工作每天发射几次,那么1个线程是很多!如果你有成千上万的工作,每分钟都有很多工作,那么你可能希望一个线程数可能更多的是50或100(这很重要,取决于你的工作所执行的工作的性质,以及你的系统资源!)。

#===============================================================        
#配置文件不是必须的,Quartz对配置项都是有默认值的,当需要自定义的时候,
#可以在classpath路径下放一个quartz.properties文件,Quartz的StdSchedulerFactory
#在启动时会自动加载该配置文件。
#===============================================================    

#===============================================================        
#1.配置主调度程序的属性        
#===============================================================    
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

#当检查某个Trigger应该触发时,默认每次只Acquire(获得)一个Trigger,(为什么要有Acquire的过程呢?是为了防止多线程访问的情况下,同一个Trigger被不同的线程多次触发)。尤其是使用JDBC JobStore时,一次Acquire就是一个update语句,尽可能一次性的多获取几个Trigger,一起触发,当定时器数量非常大的时候,这是个非常有效的优化。当定时器数量比较少时,触发不是极为频繁时,这个优化的意义就不大了。
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=50

#===============================================================        
#2.配置线程池的属性
#===============================================================          
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程池里的线程数,默认值是10,当执行任务会并发执行多个耗时任务时,要根据业务特点选择线程池的大小。可用于并发执行作业的线程数
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#===============================================================        
#3.配置JobStore的属性
#===============================================================          
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

1.配置主调度程序的属性

分别设置调度器的实例名(instanceName) 和实例 ID (instanceId)。属性 org.quartz.scheduler.instanceName 可以是你喜欢的任何字符串。默认名字一般都采用QuartzScheduler

2.配置线程池的属性

这些线程在 Quartz 中是运行在后台担当重任的。threadCount 属性控制了多少个工作者线程被创建用来处理 Job。原则上是,要处理的 Job 越多,那么需要的工作者线程也就越多。threadCount 的数值至少为 1。Quartz 没有限定你设置工作者线程的最大值,但是在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下。这项没有默认值,所以你必须为这个属性设定一个值。

threadPriority 属性设置工作者线程的优先级。优先级别高的线程比级别低的线程更优先得到执行。threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1。这个属性的正常值是 Thread.NORM_PRIORITY,为5。大多情况下,把它设置为5,这也是没指定该属性的默认值。

最后一个要设置的线程池属性是 org.quartz.threadPool.class。这个值是一个实现了 org.quartz.spi.ThreadPool 接口的类的全限名称。Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool,它能够满足大多数用户的需求。这个线程池实现具备简单的行为,并经很好的测试过。它在调度器的生命周期中提供固定大小的线程池。你能根据需求创建自己的线程池实现,如果你想要一个随需可伸缩的线程池时也许需要这么做。这个属性没有默认值,你必须为其指定值。

 

创建Scheduler定时任务步骤

1.创建任务
2.创建触发器 
3.创建调度器
4.将任务及其触发器放入调度器
5.调度器开始调度任务

 

创建要被定执行的任务类

 【1】这一步只需要创建一个实现了org.quartz.Job接口的类,并实现这个接口的唯一一个方法execute(JobExecutionContext arg0) throws JobExecutionException即可。如

package com.fync.quartz;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
 * 创建要被定执行的任务类
 * @author long
 *
 */
public class MyJob implements Job{

    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        System.out.println(sdf.format(new Date()));
//处理定时任务过渡表,到达交易开始时间,处理挂单业务
    }

}

【2】创建任务调度,并执行

     Scheduler 核心概念

  • 调度器 Scheduler
  • 任务 JobDetail,一个job对应多个tigger
  • 触发器 trigger
package test;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

/**
 * 创建任务调度,并执行
 */
public class MainScheduler {
    
    public static void schedulerJob() throws SchedulerException{
	/**
	 * //1.创建调度器 SchedulerFactory 是一个接口,用于Scheduler的创建和管理
    	SchedulerFactory schedFact = new SchefulerFactory();
    	Scheduler scheduler = schedFact.getScheduler();
    	//2.调度器开始调度任务
    	scheduler.start();
    	
    	//3.创建任务。参数分别是任务的名(唯一的),任务的组,需要执行的任务class,这个class必须是实现quartz.的job接口,接口下的execute方法就是执行的任务
    	JobDetail jobDetail = new JobDetail("TransTaskRecivweThread","GROUP_1",MyJob.class);
    	//4.创建触发器 时间格式 [秒] [分] [小时] [日] [月] [周] [年]
    	CronTrigger trigger = new CronTrigger("TransTaskRecivweThread","GROUP_1");
          //每分钟执行一次。 
    	trigger.setCronExpression("0  0/"+minute+" * * * ? *");
    	
    	//5.将任务及其触发器放入调度器。绑定 jobDetail 和 trigger,将它注册进 Scheduler 当中  ,返回值是最近一次任务执行的开始时间
    	scheduler.schedulerJob(jobDetail,trigger);
    	System.out.println("declare任务线程开启");
	 * 
	 */
    	
    	
        //1.创建任务
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
        //2.创建触发器 每3秒钟执行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3")
                            .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
                            .build();
        //3.创建调度器
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        //4.将任务及其触发器放入调度器
        scheduler.scheduleJob(jobDetail, trigger);
        //5.调度器开始调度任务
        scheduler.start();
        
    }
    
    public static void main(String[] args) throws SchedulerException {
        MainScheduler mainScheduler = new MainScheduler();
        mainScheduler.schedulerJob();
    }

}

【3】scheduler的主要函数

// 绑定 jobDetail 和 trigger,将它注册进 Scheduler 当中  ,返回值是最近一次任务执行的开始时间
Date scheduleJob(JobDetail jobDetail, Trigger trigger)
// 启动 Scheduler
void start()
// 暂停 Scheduler
void standby()
// 关闭 Scheduler
void shutdown()

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在 Spring Boot 中集成 Quartz。可以通过添加以下依赖来实现: ```xml <!-- Quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> ``` 然后,在 Spring Boot 中配置 Quartz。可以创建一个 `SchedulerFactoryBean` 实例,并设置相关属性,例如线程池大小、任务调度等等。可以在 `application.properties` 文件中设置相关属性: ```properties # Quartz Scheduler spring.quartz.job-store-type=jdbc spring.quartz.jdbc.initialize-schema=always spring.quartz.properties.org.quartz.scheduler.instanceName=QuartzScheduler spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate spring.quartz.properties.org.quartz.jobStore.useProperties=true spring.quartz.properties.org.quartz.jobStore.dataSource=myDataSource spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_ spring.quartz.properties.org.quartz.dataSource.myDataSource.driverClassName=com.mysql.jdbc.Driver spring.quartz.properties.org.quartz.dataSource.myDataSource.URL=jdbc:mysql://localhost:3306/quartz spring.quartz.properties.org.quartz.dataSource.myDataSource.user=root spring.quartz.properties.org.quartz.dataSource.myDataSource.password=root spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool spring.quartz.properties.org.quartz.threadPool.threadCount=10 spring.quartz.properties.org.quartz.threadPool.threadPriority=5 spring.quartz.properties.org.quartz.jobStore.isClustered=true spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=20000 spring.quartz.properties.org.quartz.jobStore.maxMisfiresToHandleAtATime=1 ``` 然后,需要创建一个 `Job` 类来执行具体的任务。例如: ```java public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 执行具体的任务逻辑 System.out.println("Hello Quartz!"); } } ``` 最后,需要创建一个 `CronTriggerFactoryBean` 实例,并设置相关属性。例如: ```java @Configuration public class QuartzConfig { @Bean public JobDetailFactoryBean myJobDetail() { JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(MyJob.class); factory.setDurability(true); // 任务持久化 return factory; } @Bean public CronTriggerFactoryBean myCronTrigger(@Qualifier("myJobDetail") JobDetail jobDetail) { CronTriggerFactoryBean factory = new CronTriggerFactoryBean(); factory.setJobDetail(jobDetail); factory.setCronExpression("0/5 * * * * ?"); // 每隔 5 秒触发一次 return factory; } @Bean public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("myCronTrigger") CronTrigger cronTrigger) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setTriggers(cronTrigger); return factory; } } ``` 这里的 `myJobDetail` 方法返回一个 `JobDetailFactoryBean` 实例,它指定了具体的任务类。`myCronTrigger` 方法返回一个 `CronTriggerFactoryBean` 实例,它指定了触发的具体规则。`schedulerFactoryBean` 方法返回一个 `SchedulerFactoryBean` 实例,它将任务和触发关联起来,并返回一个 `Scheduler` 实例,可以通过它来启动和停止任务调度。 最后,在 Vue 中实现调用后端接口来启动和停止任务调度。可以使用 Axios 进行 HTTP 请求。例如: ```javascript import axios from 'axios' // 启动任务调度 export function startScheduler () { return axios.post('/scheduler/start') } // 停止任务调度 export function stopScheduler () { return axios.post('/scheduler/stop') } ``` 这里的 `/scheduler/start` 和 `/scheduler/stop` 是后端接口的 URL。可以在后端使用 Spring Boot 的 MVC 模块来实现这两个接口,例如: ```java @RestController @RequestMapping("/scheduler") public class SchedulerController { @Autowired private Scheduler scheduler; @PostMapping("/start") public void start() throws SchedulerException { scheduler.start(); } @PostMapping("/stop") public void stop() throws SchedulerException { scheduler.shutdown(); } } ``` 这里的 `scheduler` 是通过 `SchedulerFactoryBean` 创建的 `Scheduler` 实例,可以通过 `start()` 和 `shutdown()` 方法来启动和停止任务调度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值