定时任务总会遇到任务重叠执行的情况,比如一个任务1分钟执行一次,而任务的执行时间超过了1分钟,这样就会有两个相同任务并发执行了。有时候我们是允许这种情况的发生的,比如任务执行的代码是幂等的,而有时候我们可能考虑到一些情况是不允许这种事情发生的。

在实际场景中,我们定时任务调度使用quartz来实现触发的,定时任务的业务代码分布在各个应用,用soa调用。

对于quartz来说,官方文档上明确对这种需求有指定的解决办法,就是使用注解@DisallowConcurrentExecution;

意思是:禁止并发执行多个相同定义的JobDetail,就是我们想要的。

下面一个实现的例子:可以对比两个job:AllowConcurrentExecutionTestJob,DisallowConcurrentExecutionTestJob

public class AllowConcurrentExecutionTestJob implements Job {
public AllowConcurrentExecutionTestJob() {
} public void execute(JobExecutionContext context) throws JobExecutionException { try {
List<JobExecutionContext> list = context.getScheduler().getCurrentlyExecutingJobs();
for(JobExecutionContext jobExecutionContext : list){
// job内部获取容器内变量
System.out.println(jobExecutionContext.getJobDetail().getKey().getName());
}
Thread.sleep(4000);
} catch (SchedulerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World! AllowConcurrentExecutionTestJob is executing.");
}
}
@DisallowConcurrentExecution
public class DisallowConcurrentExecutionTestJob implements org.quartz.Job {
public DisallowConcurrentExecutionTestJob() {
} public void execute(JobExecutionContext context) throws JobExecutionException { try {
List<JobExecutionContext> list = context.getScheduler().getCurrentlyExecutingJobs();
for(JobExecutionContext jobExecutionContext : list){
// job内部获取容器内变量
System.out.println(jobExecutionContext.getJobDetail().getKey().getName());
}
Thread.sleep(4000);
} catch (SchedulerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World! DisallowConcurrentExecutionTestJob is executing.");
}
}

测试代码:

public class QuartzTest {
public static void main(String[] args) throws InterruptedException { try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // and start it off
scheduler.start(); // define the job and tie it to our HelloJob class
JobDetail job = JobBuilder.newJob(DisallowConcurrentExecutionTestJob.class)
.withIdentity("job1", "group1")
.build(); // Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build(); // define the job and tie it to our HelloJob class
JobDetail job2 = JobBuilder.newJob(AllowConcurrentExecutionTestJob.class)
.withIdentity("job2", "group1")
.build(); // Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build(); // Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job2, trigger2);
// scheduler.scheduleJob(job2, trigger2);
// wait trigger
Thread.sleep(20000);
scheduler.shutdown(); } catch (SchedulerException se) {
se.printStackTrace();
}
}
}

我们还发现在job的execute里传参是JobExecutionContext,它可以让我们拿到正在执行的job的信息。所以我想在job里直接判断一下就可以知道有没有已经在执行的相同job。

public class SelfDisAllowConExeTestJob implements org.quartz.Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
List<JobExecutionContext> list = context.getScheduler().getCurrentlyExecutingJobs();
Set<String> jobs = new HashSet<String>();
int i=0;
for (JobExecutionContext jobExecutionContext : list){
if(context.getJobDetail().getKey().getName().equals(jobExecutionContext.getJobDetail().getKey().getName())){
i++;
}
}
if(i>1){
System.out.printf("self disallow ");
return;
}
Thread.sleep(4000);
} catch (SchedulerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World! SelfDisAllowConExeTestJob is executing."); }
}

测试代码:

public class OuartzSelfMapTest {

    public static void main(String[] args) throws InterruptedException {

        try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // and start it off
scheduler.start(); // define the job and tie it to our HelloJob class
JobDetail job = JobBuilder.newJob(SelfDisAllowConExeTestJob.class)
.withIdentity("job1", "group1")
.build(); // Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build(); // Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger); // wait trigger
Thread.sleep(20000);
scheduler.shutdown(); } catch (SchedulerException se) {
se.printStackTrace();
}
}
}

我们在实际代码中经常会结合spring,特地去看了一下MethodInvokingJobDetailFactoryBean的concurrent属性来控制是否限制并发执行的实现:

        Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
    /**
* Extension of the MethodInvokingJob, implementing the StatefulJob interface.
* Quartz checks whether or not jobs are stateful and if so,
* won't let jobs interfere with each other.
*/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public static class StatefulMethodInvokingJob extends MethodInvokingJob { // No implementation, just an addition of the tag interface StatefulJob
// in order to allow stateful method invoking jobs.
}

当然,在quartz里有一个StatefulJob,方便直接继承就实现了concurrent=false的事情了。

那么啰嗦了这么多,其实就是想表达,quartz里并没有一个可以设置说是否并发的接口,而是需要自定义的job自行继承,或使用注解来实现的。

另外,还有一个相关的注解:@PersistJobDataAfterExecution

意思是:放在JobDetail 里的JobDataMap是共享的,也就是相同任务之间执行时可以传输信息。很容易想到既然是共享的,那么就会有并发的问题,就如开头说的这个场景就会导致并发问题。所以官方文档也特别解释这个注解最好和@DisallowConcurrentExecution一起使用。

以下是例子:

@PersistJobDataAfterExecution
public class PersistJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap data = context.getJobDetail().getJobDataMap();
int i = data.getInt("P");
System.out.printf("PersistJob=>"+i);
i++;
data.put("P", i);
}
}

测试代码:

public class PersistJobDataQuartzTest {
public static void main(String[] args) throws InterruptedException { try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // and start it off
scheduler.start(); JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("P",1);
// define the job and tie it to our HelloJob class
JobDetail job = JobBuilder.newJob(PersistJob.class)
.withIdentity("job1", "group1").usingJobData(jobDataMap)
.build(); // Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build(); // Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
// wait trigger
Thread.sleep(20000);
scheduler.shutdown(); } catch (SchedulerException se) {
se.printStackTrace();
}
}
}

参考文档:

 

quartz的一些记录的更多相关文章

  1. Quartz.net使用记录

    1.引入dll文件: nuget控制台:安装quartz:Install-Package Quartz 安装log4net:Install-Package log4net,这里使用log4net记录一 ...

  2. 【Quartz】问题记录注意事项【四】

    记录一:queartz 在同时启动多个任务是,触发器名称不能设置一致,不然第二次启动会不成功 记录二:quartz 在使用任务与触发器分离写法时,任务必须要带(.StoreDurably()) IJo ...

  3. Quartz 基本概念及原理

    最近项目要用quartz,所以记录一下: 概念   Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使 ...

  4. Quartz-2D绘图之图形上下文详解

    上一篇文章大概描述了下Quartz里面大体所包含的东西,但是对具体的细节实现以及如何调用相应API却没有讲.这篇文章就先讲讲图形上下文(Graphics Context)的具体操作. 所谓Graphi ...

  5. iPhone之Quartz 2D系列--图形上下文(2)(Graphics Contexts)

    以下几遍关于Quartz 2D博文都是转载自:http://www.cocoachina.com/bbs/u.php?action=topic&uid=38018 iPhone之Quartz ...

  6. quartz 定时任务的实现

    需求:项目中有一个任务,当时间到了会向移动端通过百度云推送推送信息,之前很傻叉的是写一个多线程一直扫描,每分钟扫描一次,比对当前时间和任务时间是否一样,结果把 项目跑死了,项目中用了一个简单的quar ...

  7. Quartz 2D - 图形上下文(Graphics Contexts)

    一个Graphics Context表示一个绘制目标.它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息 ...

  8. Quartz.NET学习系列

    Quartz.NET它是一个开源的任务调度引擎,对于周期性任务,持久性任务提供了很好的支持,并且支持持久性.集群等功能. 这是什么对我来说Quartz.NET学习记录: 源代码下载http://yun ...

  9. Quartz 2D编程指南(2) - 图形上下文

    一个Graphics Context表示一个绘制目标.它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息 ...

随机推荐

  1. Codeforces Round #326 (Div. 2) B

    1.每一个合数都可以由若干个素数相乘而得到 2.质因数知识 :求一个数因数的个数等于它的每个质因数的次数加一的和相乘的积因为质因数可以不用,所以要加一.例如6=2x3,两个质因数都是一次,如果两个质因 ...

  2. mysql中配置ssl_key、ssl-cert、ssl-ca的路径及建立ssl连接

    1.创建 CA 私钥和 CA 证书 (1)下载并安装openssl,将bin目录配置到环境变量: (2)设置openssl.cfg路径(若不设置会报错,找不到openssl配置文件) \bin\ope ...

  3. mysql 手册关于修改列字符编码的一个bug

    项目因为历史原因使用了 GBK编码,遇到非GBK编码字符时出现乱码问题,情况比较严重,暂时先打算修改 列的字符编码为 utf8mb4. 查看 mysql 手册: 用 GBK 编码转 utf8 进行说明 ...

  4. 安装JDK出现错误:-bash: /usr/java/jdk1.7.0_71/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory解决办法

    1.错误描述:安装好jdk之后,通过java -version,javac,java等命令测试是否安装成功时出现错误-bash: /usr/java/jdk1.7.0_71/bin/java: /li ...

  5. ECharts插件的使用

    ECharts插件:官网下载echarts.js开发者可以选择源码.下载地址:http://echarts.baidu.com/download.html 下载之后,echarts.js放在js文件夹 ...

  6. 从零开始学习前端开发 — 7、CSS宽高自适应

    一.宽度自适应 语法:width:100%; 注: a)块状元素的默认宽度为100% b) 当给元素设置宽度为100%时,继承父元素的宽度 c) 通常使用宽度自适应实现通栏效果 二.高度自适应 语法: ...

  7. 【转载备忘】PowerDesigner16.5基本使用

    这两天都在设计数据库,使用了powerdesigner进行设计的,然后摸索了好久,本来打算写一篇文章来记述一下的,写了一半,突然发现网上早就有比我写的好的文章了,所有删了之前写的,直接贴出来那个文章的 ...

  8. 记node前后端代码共用的一次坑

    项目背景 nodejs项目,webpack打包,用axios请求,Promise封装,nunjucks模板引擎: 之前已将nunjucks模板通过webpack打包策略,做成前后端共用: 目前需要将网 ...

  9. [基础常识]申请免费SSL证书 - 阿里云云盾证书 - Digicert+Symantec 免费型DV SSL

    https://bbs.aliyun.com/read/573933.html?spm=5176.10695662.1996646101.searchclickresult.72be06dct9Qvw ...

  10. dede的pagelist标签的listsize数字属性详解

    转载▼http://blog.sina.com.cn/s/blog_a4f3bd4e01012c8n.html dede的pagelist标签的listsize数字属性详解.见远seo经常用织梦搭建各 ...