原文连接:https://www.cnblogs.com/jing99/p/11546559.html
html
Scheduledjava
只适合处理简单的计划任务,不能处理分布式计划任务。优点:是spring框架提供的计划任务,开发简单,执行效率比较高。且在计划任务数量太多的时候,可能出现阻塞,崩溃,延迟启动等问题。
mysql
Scheduled定时任务是spring3.0版本以后自带的一个定时任务。其所属Spring的资源包为:spring-context-support。因此须要使用Scheduled定时任务机制时,须要在工程中依赖对应资源,具体以下:web
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
dependency>
若是在spring应用中须要启用Scheduled定时任务,则须要在启动类上增长注解@EnableScheduling,表明启用Scheduled定时任务机制。具体以下:
spring
@SpringBootApplication
@EnableScheduling
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
Scheduled定时任务的核心在于注解@Scheduled,这个注解的核心属性是cron,表明定时任务的触发计划表达式。这个表达式的格式为:sql
@Scheduled(cron="seconds minutes hours day month week")
或
数据库
@Scheduled(cron="seconds minutes hours day month week year")
推荐使用第一种表达式形式,由于在不少其余技术中都有不一样的定时任务机制,其中用于设置触发计划的表达式都是第一种cron表达式。第二种表达式不能说是Spring Scheduled特有的,也是只有少数技术支持的。编程
cron表达式中,每一个位置的约束以下:api
星号(*):可用在全部字段中,表示对应时间域的每个时刻,例如,*在分钟字段时,表示“每分钟”;微信
问号(?):该字符只在日期和星期字段中使用,它一般指定为“无心义的值”,至关于占位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在秒数字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可使用*/y,它等同于0/y;
L:该字符只在日期和星期字段中使用,表明“Last”的意思,但它在两个字段中意思不一样。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;若是L用在星期中,则表示星期六,等同于7。可是,若是L出如今星期字段里,并且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
W:该字符只能出如今日期字段里,是对前导日期的修饰,表示离该日期最近的工做日。例如15W表示离该月15号最近的工做日,若是该月15号是星期六,则匹配14号星期五;若是15日是星期日,则匹配16号星期一;若是15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不可以跨月,如你指定1W,若是1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
LW组合:在日期字段能够组合使用LW,它的意思是当月的最后一个工做日;
井号(#):该字符只能在星期字段中使用,表示当月某个工做日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
C:该字符只在日期和星期字段中使用,表明“Calendar”的意思。它的意思是计划所关联的日期,若是日期没有被关联,则至关于日历中全部日期。例如5C在日期字段中就至关于日历5日之后的第一天。1C在星期字段中至关于星期往后的第一天。
Cron表达式对特殊字符的大小写不敏感,对表明星期的缩写英文大小写也不敏感。
计划任务Scheduled是经过一个线程池实现的。是一个多线程的调度。SpringBoot会初始化一个线程池,线程池默认大小为1,专门用于执行计划任务。每一个计划任务启动的时候,都从线程池中获取一个线程执行,若是发生异常,只是执行当前任务的线程发生异常,而不是计划任务调度线程发生异常。若是当前定时任务还未执行完成,当相同的定时任务又进入到执行周期时,不会触发新的定时任务。如:
@Scheduled(cron="* * * * * ?")
public void test1(){
Random r = new Random();
/*int i = r.nextInt(100);
if(i % 3 == 0){
throw new RuntimeException("error");
}*/
System.out.println(Thread.currentThread().getName() + " cron=* * * * * ? --- " + new Date());
try{
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
}
如结果所示(每次的线程名称一致,因为前一个定时任务未执行完成,所以形成后一个任务的推迟,而不是1秒执行一次,而是3秒):
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:20 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:23 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:26 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:29 CST 2019
quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它能够与J2EE与J2SE应用程序相结合也能够单独使用。Quartz能够用来建立简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。
Quartz是一个彻底由java编写的开源做业调度框架。不要让做业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!
在开发Quartz相关应用时,只要定义了Job(任务),Trigger(触发器)和Scheduler(调度器),便可实现一个定时调度能力。其中Scheduler是Quartz中的核心,Scheduler负责管理Quartz应用运行时环境,Scheduler不是靠本身完成全部的工做,是根据Trigger的触发标准,调用Job中的任务执行逻辑,来完成完整的定时任务调度。
Job - 定时任务内容是什么。
Trigger - 在什么时间上执行job。
Scheduler - 维护定时任务环境,并让触发器生效。
在SpringBoot中应用Quartz,须要依赖下述资源:
<dependency>
<groupId>org.springframework
groupId>
<artifactId>spring-context-support
artifactId>
dependency>
<dependency>
<groupId>org.quartz-scheduler
groupId>
<artifactId>quartz
artifactId>
<version>2.2.1
version>
<exclusions>
<exclusion>
<artifactId>slf4j-api
artifactId>
<groupId>org.slf4j
groupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework
groupId>
<artifactId>spring-tx
artifactId>
dependency>
dependencies>
启动器添加注解@EnableScheduling:
/**
* @EnableScheduling 必要
* 开启定时任务机制。
*/
@SpringBootApplication
@EnableScheduling
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
定义JOB任务以及JOB任务调用的模拟业务对象:
public class SpringBootQuartzJobDemo implements Job {
// 用于模拟任务中的业务对象。也多是数据访问对象,或其余类型的对象。
@Autowired
private CommonsUtil4Quartz commonsUtil4Quartz;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("SpringBootQuartzJobDemo : " + new Date());
this.commonsUtil4Quartz.testMethod();
}
}
@Component
public class CommonsUtil4Quartz {
public void testMethod(){
System.out.println("CommonsUtil4Quartz testMethod run...");
}
}
建立Trigger以及JobDetail对象,并用Schedule配置定时任务:
/**
* 初始化类
* Quartz环境初始化。
*
*/
@Configuration
public class QuartzConfiguration {
/**
* 建立Job对象。在Spring环境中,建立一个类型的对象的时候,不少状况下,都是经过FactoryBean来间接建立的。
* 若是有多个Job对象,定义屡次方法。
*
* 在JobDetailFactoryBean类型中,用于建立JobDetail对象的方法,其底层使用的逻辑是:Class.newInstance()
* 也就是说,JobDetail对象不是经过Spring容器管理的。
* 由于Spring容器无论理JobDetail对象,那么Job中须要自动装配的属性,就没法实现自动状态。如上JOB的第10行会报空指针异常。
*
* 解决方案是:将JobDetail加入到Spring容器中,让Spring容器管理JobDetail对象。
* 须要重写Factory相关代码。实现Spring容器管理JobDetail。
* @return
*/
@Bean
public JobDetailFactoryBean initJobDetailFactoryBean(){
JobDetailFactoryBean factoryBean =
new JobDetailFactoryBean();
// 提供job类型。
factoryBean.setJobClass(SpringBootQuartzJobDemo.class);
return factoryBean;
}
/**
* 管理Trigger对象
* CronTrigger - 就是Trigger的一个实现类型。其中用于定义周期时间的是CronSchedulerBuilder
* 实际上,CronTrigger是用于管理一个Cron表达式的类型。
* @param jobDetailFactoryBean - 上一个方法初始化的JobDetailFactoryBean
* @return
*/
@Bean(name="cronTriggerFactoryBean1")
public CronTriggerFactoryBean initCronTriggerFactoryBean(
){
CronTriggerFactoryBean factoryBean =
new CronTriggerFactoryBean();
JobDetailFactoryBean jobDetailFactoryBean = this.initJobDetailFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
factoryBean.setCronExpression("0/3 * * * * ?");
return factoryBean;
}
/**
* 初始化Scheduler
* @param cronTriggerFactoryBean - 上一个方法初始化的CronTriggerFactoryBean
* @return
*/
@Bean
public SchedulerFactoryBean initSchedulerFactoryBean(
CustomJobFactory customJobFactory,
CronTriggerFactoryBean[] cronTriggerFactoryBean){
SchedulerFactoryBean factoryBean =
new SchedulerFactoryBean();
CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
for(int i = 0; i < cronTriggerFactoryBean.length; i++){
triggers[i] = cronTriggerFactoryBean[i].getObject();
}
// 注册触发器,一个Scheduler能够注册若干触发器。
factoryBean.setTriggers(triggers);
// 为Scheduler设置JobDetail的工厂。能够覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效。
factoryBean.setJobFactory(customJobFactory);
return factoryBean;
}
}
重写JobFactory:
/**
* 重写的工厂对象。
*/
@Component
public class CustomJobFactory extends AdaptableJobFactory {
/**
* AutowireCapableBeanFactory : 简单理解为Spring容器,是Spring容器Context的一个Bean对象管理工程。
* 能够实现自动装配逻辑,和对象建立逻辑。
* 是SpringIoC容器的一个重要组成部件。
*/
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 经过父类型中的方法,建立JobDetail对象。
Object obj = super.createJobInstance(bundle);
// 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。
this.autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
分布式quartz配置
一、资源依赖配置:因为分布式的缘由,Quartz中提供分布式处理的jar包以及数据库及链接相关的依赖。
<dependency>
<groupId>org.springframework
groupId>
<artifactId>spring-context-support
artifactId>
dependency>
<dependency>
<groupId>org.quartz-scheduler
groupId>
<artifactId>quartz
artifactId>
<version>2.2.1
version>
<exclusions>
<exclusion>
<artifactId>slf4j-api
artifactId>
<groupId>org.slf4j
groupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.quartz-scheduler
groupId>
<artifactId>quartz-jobs
artifactId>
<version>2.2.1
version>
dependency>
<dependency>
<groupId>org.springframework
groupId>
<artifactId>spring-tx
artifactId>
dependency>
<dependency>
<groupId>org.springframework.boot
groupId>
<artifactId>spring-boot-starter-web
artifactId>
dependency>
<dependency>
<groupId>org.springframework.boot
groupId>
<artifactId>spring-boot-starter-data-jpa
artifactId>
dependency>
<dependency>
<groupId>mysql
groupId>
<artifactId>mysql-connector-java
artifactId>
dependency>
<dependency>
<groupId>com.alibaba
groupId>
<artifactId>druid
artifactId>
<version>1.0.9
version>
dependency>
dependencies>
二、提供数据库相关配置:
spring.datasource.url=jdbc:mysql://localhost:3306/quartz?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=600000
spring.datasource.timeBetweenEvictionRunsMillis=600000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
server.port=8080
三、提供Quartz配置信息,为Quartz提供数据库配置信息,如数据库,表格的前缀之类的。
# 是否使用properties做为数据存储
org.quartz.jobStore.useProperties=false
# 数据库中的表格命名前缀
org.quartz.jobStore.tablePrefix = QRTZ_
# 是不是一个集群,是否是分布式的任务
org.quartz.jobStore.isClustered = true
# 集群检查周期,单位毫秒。能够自定义缩短期。当某一个节点宕机的时候,其余节点等待多久后开始执行任务。
org.quartz.jobStore.clusterCheckinInterval = 5000
# 单位毫秒, 集群中的节点退出后,再次检查进入的时间间隔。
org.quartz.jobStore.misfireThreshold = 60000
# 事务隔离级别
org.quartz.jobStore.txIsolationLevelReadCommitted = true
# 存储的事务管理类型
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 使用的Delegate类型
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 集群的命名,一个集群要有相同的命名。
org.quartz.scheduler.instanceName = ClusterQuartz
# 节点的命名,能够自定义。AUTO表明自动生成。
org.quartz.scheduler.instanceId= AUTO
# rmi远程协议是否发布
org.quartz.scheduler.rmi.export = false
# rmi远程协议代理是否建立
org.quartz.scheduler.rmi.proxy = false
# 是否使用用户控制的事务环境触发执行job。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
四、初始化数据库
建表语句能够本身在官方网站中查找(Quartz-lib中),使用tables-mysql.sql建表。
五、定义JOB类
启动器和普通quartz无差别,可是JOB自身定义有些许差别:
/**
* 使用Spring提供的Quartz相关Job类型实现Job的定义。
* 父类型QuartzJobBean中,提供了分布式环境中任务的配置定义。
* 保证分布式环境中的任务是有效的。
*/
@PersistJobDataAfterExecution // 当job执行结束,持久化job信息到数据库
@DisallowConcurrentExecution // 保证job的惟一性(单例)
public class SpringBootQuartzJobDemo extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("SpringBootQuartzJobDemo : " + new Date());
}
}
六、QuartzConfiguration类型定义
@Configuration
public class QuartzConfiguration {
@Autowired
private DataSource dataSource;
/**
* 建立调度器, 能够省略的。
* @return
* @throws Exception
*/
@Bean
public Scheduler scheduler() throws Exception {
Scheduler scheduler = schedulerFactoryBean().getScheduler();
scheduler.start();
return scheduler;
}
/**
* 建立调度器工厂bean对象。
* @return
* @throws IOException
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setSchedulerName("Cluster_Scheduler");
factory.setDataSource(dataSource);
factory.setApplicationContextSchedulerContextKey("applicationContext");
// 设置调度器中的线程池。
factory.setTaskExecutor(schedulerThreadPool());
// 设置触发器
factory.setTriggers(trigger().getObject());
// 设置quartz的配置信息
factory.setQuartzProperties(quartzProperties());
return factory;
}
/**
* 读取quartz.properties配置文件的方法。
* @return
* @throws IOException
*/
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/**
* 建立Job对象的方法。
* @return
*/
@Bean
public JobDetailFactoryBean job() {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(SpringBootQuartzJobDemo.class);
// 是否持久化job内容
jobDetailFactoryBean.setDurability(true);
// 设置是否屡次请求尝试任务。
jobDetailFactoryBean.setRequestsRecovery(true);
return jobDetailFactoryBean;
}
/**
* 建立trigger factory bean对象。
* @return
*/
@Bean
public CronTriggerFactoryBean trigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(job().getObject());
cronTriggerFactoryBean.setCronExpression("0/2 * * * * ?");
return cronTriggerFactoryBean;
}
/**
* 建立一个调度器的线程池。
* @return
*/
@Bean
public Executor schedulerThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(25);
executor.setQueueCapacity(100);
return executor;
}
}
若JOB任务有定义调用业务等内容,也须要重写JobFactory,如上述常规quartz,此处再也不赘述。
END
我是武哥,最后给你们 免费分享我写的 10 万字 Spring Boot 学习笔记(带完整目录)以及对应的源码 。这是我以前在 CSDN 开的一门课,因此笔记很是详细完整,我准备将资料分享出来给你们免费学习,相信你们看完必定会有所收获( 下面有下载方式 )。
能够看出,我当时备课很是详细,目录很是完整,读者能够手把手跟着笔记,结合源代码来学习。如今免费分享出来,有须要的读者能够下载学习,就在我公众号回复:笔记,就行。
若有文章对你有帮助,
在看和转发是对我最大的支持!
关注Java开发宝典
天天学习技术干货
点赞是最大的支持
本文分享自微信公众号 - 武哥聊编程(eson_15)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。